didac
didac

Reputation: 83

C++ multiple inheritance with base classes deriving from the same class

I have stumbled on a problem while trying to re-use code from different classes. I post it here in hope that some of you might be able to help me.

I have a set of classes (B,C) deriving from the same class (A) which forces the implementation of some methods (foo, run). Class B implements these method, and both B and C provide other methods:

#include<iostream>

template<class I, class O>
class A {
public:
    A() {}
    virtual ~A() {}

    virtual void foo() const = 0;     // force implementation of this function
    virtual void run() const = 0;     // force implementation of this function
};

template<class I, class O>
class B : public A<I,O> {
public:
    B() {}
    virtual ~B() {}

    virtual void foo() const {        // implementation for the Base class
        std::cout << "B's implementation of foo" << std::endl;
    }

    virtual void run() const {        // implementation for the Base class
        std::cout << "B's implementation of run" << std::endl;
    }

    virtual void foobar() const {     // some other function provided by this class
        std::cout << "B's implementation of foobar" << std::endl;
    }
};

template<class I, class O, class M>
class C : public A<I,O> {
public:
    C() {}
    virtual ~C() {}

    virtual void bar(M m) const {     // some other function provided by this class
        std::cout << "C's implementation of bar with: " << m << std::endl;
    }
};

Now, what I am trying to do is inherit from both B and C so that I can have the extra methods (foobar, bar), but also not have to implement the method from class A (foo) because it is already defined in B:

template<class I, class O>
class D : public B<I,O>, public C<I,O,int> {
public:
    D() {}

    void run() const {
        this->bar(123);
        this->foo();
        this->foobar();
    }
};

But for some reason the compiler gives me this error:

test.cpp: In function ‘int main(int, char**)’: test.cpp:68:35: error: cannot allocate an object of abstract type ‘D<float, double>’

A<float, double> *d = new D<float, double>(); // what I need to do

test.cpp:48:11: note: because the following virtual functions are pure within ‘D<float, double>’:

class D : public B<I,O>, public C<I,O,int> {

    ^

test.cpp:9:22: note: void A<I, O>::foo() const [with I = float; O = double]

virtual void foo() const = 0; // force implementation of this function

This is the code I use to run it:

int main(int argc, char **argv)
{

    A<float, double> *b = new B<float, double>();
    b->foo();                                            // prints "B's implementation of foo"
    b->run();                                            // prints "B's implementation of run"

    //A<float, double> *c = new C<float, double, int>(); // obviously fails because C does not implement any of A's functions

    //A<float, double> *d = new D<float, double>;        // line 68: what I need to do
    //d->run();                                          // ***throws the abstract class error

    return 0;
}

I want to use the 'run' function of an object of class D from a pointer to a A. As all the functions are virtual I expect to execute implementation of each function defined in the lowest inheritance point, meaning that 'B::run' will be discarded. As 'D::run' uses functions from both B and C I need to inherit from both classes.

I hope I have described it enough and not confused anybody. Thanks for the help!

Upvotes: 5

Views: 4349

Answers (3)

RobC
RobC

Reputation: 511

I know this is a late answer but since you are deriving from a pure virtual function for class C, you have to implement it, then in those functions you call the base class:

virtual void foo() const {      // for class C  
    B::foo();
}

Upvotes: 0

R Sahu
R Sahu

Reputation: 206607

The answer by @cdhowie gives you a solution.

To understand the problem the compiler is complaining about, take a set of simpler classes:

struct A
{
   virtual void foo() = 0;
};

struct B : A
{
   virtual void foo() {}
}

struct C : A
{
   void bar() {}
}

struct D : B, C
{
};

The class hierarchy of D is:

A    A
|    |
B    C
 \   /
   D

With this inheritance structure, D has two virtual tables, one corresponding to the B inheritance hierarchy and one corresponding to C inheritance hierarchy. The difference being that in the B hierarchy, there is an implementation of A::foo() while there isn't one in the C hierarchy.

Let's say you were allowed to construct an object of type D.

D d;
C* cp = &d;

Now cp points to the C hierarchy of D, and uses a virtual table in which foo is not implemented. That will be a run time error that the compiler is helping you avoid at compile time.

Upvotes: 2

cdhowie
cdhowie

Reputation: 169018

If you change B and C to virtually inherit from the A template class, they will share a single base instance when combined by D and this error will go away:

template<class I, class O>
class B : virtual public A<I,O> {

// ...

template<class I, class O, class M>
class C : virtual public A<I,O> {

However, this pattern (known as the diamond inheritance (anti-)pattern) can be very difficult to reason about and I would strongly suggest avoiding it if possible. You are likely to run into even more obscure problems later.


Here is a sample of this technique working, but showing some results that may not be expected at first glance:

class A {
public:
    virtual void foo() = 0;
};

class B : virtual public A {
public:
    virtual void foo() override;
};

void B::foo()
{
    std::cout << "B::foo()" << std::endl;
}

class C : virtual public A { };

class D : public B, public C { };

int main() {
    D d;
    C & c = d;

    c.foo();

    return 0;
}

Note that even though you are calling C::foo(), which is pure virtual, since there is only one A instance the inherited pure virtual function resolves to B::foo() though the shared A vtable. This is a somewhat surprising side-effect -- that you can wind up invoking methods implemented on a cousin type.

Upvotes: 6

Related Questions