Reputation: 51
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
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
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
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