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.
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.