Seth Travis
Seth Travis

Reputation: 51

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.

Upvotes: 1

Views: 1029

Answers (3)

Seth Travis
Seth Travis

Reputation: 51

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.

Upvotes: 0

Revelnaut
Revelnaut

Reputation: 11

My main concern with the design of your class structure here is that in the example you've provided it seems redundant for PureVirtualDerivedBase to be inheriting from PureVirtualBase. Extending an abstract class by deriving from it in another abstract class can make your inheritance tree confusing and in a worst case scenario you can end up having to create duplicate implementations for the abstract methods you've declared in the base class.

In this case you would have to reimplement IntFn() in Bar since inheriting PureVirtualDerivedBase explicitly requires you to implement all virtual methods associated with it, regardless wether or not they've been implemented in Foo.

My suggestion is to remove PureVirtualBase inheritance from PureVirtualDerivedBase and only inherit an abstract class in a class that actually implements the abstract methods. For clarity it's also good practice to avoid making interfaces dependant on other interfaces when it can be avoided. Instead try to make interfaces as self-contained as possible and inherit from multiple interfaces when you wish to implement different functionality for a derived class.

Upvotes: 1

Raviteja Narra
Raviteja Narra

Reputation: 456

This is a slight variation of the diamond problem. basically in c++, each inheritance path is followed separately, which means the class Bar has a copy of Foo and a copy of PureVirtualDerivedBase. This explains the problem you are facing.

However, I don't see the point of explicitly deriving Bar from PureVirtualDerivedBase as Foo already has it.

Upvotes: 0

Related Questions