Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple inheritance and pure virtual functions

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 :)

like image 320
HighCommander4 Avatar asked Jan 02 '12 00:01

HighCommander4


People also ask

Can multiple inheritance have virtual function?

Virtual inheritance is used when we are dealing with multiple inheritance but want to prevent multiple instances of same class appearing in inheritance hierarchy. From above example we can see that “A” is inherited two times in D means an object of class “D” will contain two attributes of “a” (D::C::a and D::B::a).

Are pure virtual functions inherited?

Virtual member functions are inherited. A class derived from an abstract base class will also be abstract unless you override each pure virtual function in the derived class. The compiler will not allow the declaration of object d because D2 is an abstract class; it inherited the pure virtual function f() from AB .

How are virtual functions related to inheritance?

Base classes can't inherit what the child has (such as a new function or variable). Virtual functions are simply functions that can be overridden by the child class if the that child class changes the implementation of the virtual function so that the base virtual function isn't called. A is the base class for B,C,D.

What is a pure virtual function?

A pure virtual function or pure virtual method is a virtual function that is required to be implemented by a derived class if the derived class is not abstract. Classes containing pure virtual methods are termed "abstract" and they cannot be instantiated directly.


2 Answers

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.

like image 186
bdonlan Avatar answered Oct 18 '22 03:10

bdonlan


The usual C++ idiom is:

  • public virtual inheritance for interface classes
  • private non-virtual inheritance for implementation classes

In this case we would have:

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 : private implementation_base,
                        virtual public interface
{   
    void bar();
};

In implementation, the unique interface_base virtual base is :

  • publicly inherited via interface: implementation --public--> interface --public--> interface_base
  • privately inherited via implementation_base: implementation --private--> implementation_base --public--> interface_base

When client code does one of these derived to base conversions:

  • derived to base pointer conversions,
  • reference binding of base type with an initializer of static type derived,
  • access to inherited base class members via a lvalue of derived static type,

what matters is only that there is a least one accessible inheritance path from the derived class to the given base class subobject; other inaccessible paths are simply ignored. Because inheritance of the base class is only virtual here, there is only one base class subject so these conversions are never ambiguous.

Here, the conversion from implementation to interface_base, can always be done by client code via interface; the other inaccessible path does not matter at all. The unique interface_base virtual base is publicly inherited from implementation.

In many cases, the implementation classes (implementation, implementation_base) will be kept hidden from client code: only pointers or references to the interface classes (interface, interface_base) will be exposed.

like image 27
curiousguy Avatar answered Oct 18 '22 01:10

curiousguy