Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Method in base class that returns derived class type?

I have a bunch of classes that have one function in common, except that it returns a pointer to their own type. The code looks the same and I would like to move that into an abstract base class. But how can I make classes that inherit from it return their own type?

class base {
    base *foo() {
        // ...
    }
};


class derived : public base {

};

derived d;
d.foo(); // Should return derived* instead of base*

Is there a way to express this in C++?

like image 650
danijar Avatar asked Aug 24 '14 10:08

danijar


2 Answers

Yes, C++ supports this. It's called covariant return types. You just have to declare the function virtual and declare the return types accordingly. That's all there is to it.

struct base {
    virtual base *foo() {
        // ...
    }
};


struct derived : public base {
    virtual derived *foo() {
        // ...
    }
};

derived d;
base *base_ptr = d.foo();

Now a comment of yours extends the original question:

But actually my goal is to not repeat the function body since it's the same except the used type.

This is not possible.

There are various techniques which can facilitate the repeating, but you won't get around the fact that whatever you do, you still have to create the function bodies yourself.

One such technique would be using macros, at the cost of obfuscation and all other disadvantages that come with macros; but still, the macro won't appear in the classes automatically. You have to put it there.

// beware of macros!
#define FOO(T) virtual T *foo() { return new T; }

struct base {
    FOO(base)
    virtual ~base() {} // let's not forget the virtual destructor
};


struct derived : public base {
    FOO(derived)
};

A similar approach is to facilitate the repeating of the function bodies with a template:

template <class T>
T *ComplicatedFunctionReturningT()
{
    T *t;
    // ...
    // ...
    // ...
    return t;
}

struct base {
    virtual base *foo() {
        return ComplicatedFunctionReturningT<base>();
    }
    virtual ~base() {} // let's not forget the virtual destructor
};


struct derived : public base {
    virtual derived *foo() {
        return ComplicatedFunctionReturningT<derived>();
    }
};

Templates are safer then macros.


Another approach is to use the Template Method design pattern. If a lot of code is repeated in the function body of every class, try to move as much as possible in the base class and put the small abstract part into a private function to be overridden:

class base {
public:
    base *foo() { // no longer virtual
        // ...
        // ...
        base *ptr = fooImpl();
        // ...
        // ...
        return ptr;
    }

    virtual ~base() {} // let's not forget the virtual destructor

private:
    virtual base *fooImpl() = 0; // pure virtual and private
};

class derived1 : public base {
private:
    virtual derived1 *fooImpl() {
        return new derived1; // very simple body
    }
};

class derived2 : public base {
private:
    virtual derived2 *fooImpl() {
        return new derived2; // very simple body
    }
};

Of course, all of this is really only worth it if the function bodies are really complicated. For extreme cases, an entirely different approach is to generate the C++ code using some external tool or script.

And finally, if this is really a problem, reconsider your design as a whole. Perhaps it turns out you don't really need the function, or you don't need OOP for the actual problem your program attempts to solve.

like image 196
Christian Hackl Avatar answered Nov 09 '22 14:11

Christian Hackl


Based on your comment to Christians answer, you could implement a template helper method to not duplicate code you would be using:

class base 
{
  protected:
    template<class T> T* fooInternal()
    {
        T* t = new T();

        // do stuff with t

        return t;
    }
  public:
    virtual base* foo() { return fooInternal<base>(); }
};


class derived : public base 
{
  public:
    virtual derived* foo() { return fooInternal<derived>(); }
};
like image 35
nvoigt Avatar answered Nov 09 '22 14:11

nvoigt