Multiple inheritance and pure virtual functions

HighCommander4 picture HighCommander4 · Jan 2, 2012 · Viewed 10.6k times · Source

The following code:

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

struct interface : public interface_base
{
    virtual void bar() = 0;
};

struct implementation_base : public interface_base
{
    void foo();
};

struct implementation : public implementation_base, public interface
{   
    void bar();
};

int main()
{
    implementation x;
}

fails to compile with the following errors:

test.cpp: In function 'int main()':
test.cpp:23:20: error: cannot declare variable 'x' to be of abstract type 'implementation'
test.cpp:16:8: note:   because the following virtual functions are pure within 'implementation':
test.cpp:3:18: note:    virtual void interface_base::foo()

I have played around with it and figured out that making the 'interface -> interface_base' and 'implementation_base -> interface_base' inheritances virtual, fixes the problem, but I don't understand why. Can someone please explain what is going on?

p.s. I omitted the virtual destructors on purpose to make the code shorter. Please don't tell me to put them in, I already know :)

Answer

bdonlan picture bdonlan · Jan 2, 2012

You have two interface_base base classes in your inheritance tree. This means you must provide two implementations of foo(). And calling either of them will be really awkward, requiring multiple casts to disambiguate. This usually is not what you want.

To resolve this, use virtual inheritance:

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

struct interface : virtual public interface_base
{
    virtual void bar() = 0;
};

struct implementation_base : virtual public interface_base
{
    void foo();
};

struct implementation : public implementation_base, virtual public interface
{   
    void bar();
};

int main()
{
    implementation x;
}

With virtual inheritance, only one instance of the base class in question is created in the inheritance heirarchy for all virtual mentions. Thus, there's only one foo(), which can be satisfied by implementation_base::foo().

For more information, see this prior question - the answers provide some nice diagrams to make this all more clear.