Search code examples
c++multiple-inheritancepure-virtualvirtual-inheritance

Multiple inheritance with two interfaces, one which derives from first, and a derived class implementing the first interface


Sorry for the poor description. Here's the issue:

class PureVirtualBase {
public:
    virtual ~PureVirtualBase() {}
    virtual int IntFn() = 0;
};

class PureVirtualDerivedBase : public PureVirtualBase {
public:
    virtual ~PureVirtualDerivedBase() {}
    virtual int OtherIntFn() = 0;
};

class Foo : public PureVirtualBase {
public:
    virtual int IntFn() { return intVal; }
protected:
    int intVal;
};

class Bar : public Foo, public PureVirtualDerivedBase {
public
    virtual int OtherIntFn() { return 123; }
};

This fails due to "'Bar": cannot instantiate abastract class due to following members: 'int PureVirtualBase::IntFn(void) is abstract"

I'm not really sure how to correct this. I would have thought that Bar was fine since it is inheriting Foo's implementation of IntFn (and adding OtherIntFn as required by PureVirtualDerivedBase). I've tried making the inheritance virtual (public virtual Foo, public virtual PureVirtualBase), but that didn't work.

Any ideas? Thanks in advance.


Solution

  • The mistake is in how virtual inheritance is being applied.

    Specifically, this problem can be fixed by using virtual inheritence, just not in the way first described (i.e. not "public virtual Foo, public virtual PureVirtualBase").

    Both Foo and PureVirtualDerivedBase need to virtually inherit from PureVirtualBase:

    class PureVirtualBase {
    public:
        virtual ~PureVirtualBase() {}
        virtual int IntFn() = 0;
    };
    
    class PureVirtualDerivedBase : public virtual PureVirtualBase {
    public:
        virtual ~PureVirtualDerivedBase() {}
        virtual int OtherIntFn() = 0;
    };
    
    class Foo : public virtual PureVirtualBase {
    public:
        virtual int IntFn() { return intVal; }
    protected:
        int intVal;
    };
    
    class Bar : public Foo, public PureVirtualDerivedBase {
    public
        virtual int OtherIntFn() { return 123; }
    };
    

    In this way, Bar can add an implementation of OtherIntFn to Foo without losing Foo's implementation of IntFn.

    Raviteja is correct that this is a variation of the diamond problem. However, we need Bar to inherit from PureVirtualDerivedBase to be able to add the additional functionality of OtherIntFn.

    I agree with Revelnaut that it is good try to avoid this sort of thing, but there are some instances where doing so can be awkward / inconvenient, particularly when one interface is clearly a subset of another interface.

    Here is a more tangible example:

    class IList {
    public:
        virtual ~IList() {}
        virtual int GetLength() = 0;
    };
    
    class IDynamicList : public virtual IList {
        virtual ~IDynamicList() {}
        virtual int GetCapacity() = 0;
    };
    
    class ListOfFoo : public virtual IList {
    public:
        virtual ~ListOfFoo() {}
        virtual int GetLength() { return length; }
        void UsefulFooFunction();
    
    protected:
        int length;
    };
    
    class DynamicFooList : public ListOfFoo, public IDynamicList {
    public:
        virtual ~DynamicFooList() {}
        virtual int GetCapacity() { return capacity; }
        void FooSpecificFnThatGrowsList();
    
    protected:
        int capacity;
    };
    

    In this case, IList's functionality is a subset of IDynamicList's functionality.

    You could separate the interfaces, but then a pointer to IDynamicList won't be able to tell you the length of the list; it would only be able to tell you the capacity. In this case, it would be cumbersome to pass / retrieve an additional pointer to to get the functionality provided by IList when IDynamicList is clearly already a list.