The following code:
struct interface_base
{
virtual void foo() = 0;
};
struct interface : public interface_base
{
virtual void bar() = 0;
};
struct
For the case of 'solving' the diamond inheritance problem, the solutions offered by bdonlan are valid. Having said that, you can avoid the diamond-problem with design. Why must every instance of a given class be seen as both classes? Are you ever going to pass this same object to a class that says something like:
void ConsumeFood(Food *food);
void ConsumeDrink(Drink *drink);
class NutritionalConsumable {
float calories() = 0;
float GetNutritionalValue(NUTRITION_ID nutrition) = 0;
};
class Drink : public NutritionalConsumable {
void Sip() = 0;
};
class Food : public NutritionalConsumable {
void Chew() = 0;
};
class Icecream : public Drink, virtual public Food {};
void ConsumeNutrition(NutritionalConsumable *consumable) {
ConsumeFood(dynamic_cast<Food*>(food));
ConsumeDrink(dynamic_cast<Drink*>(drink));
}
// Or moreso
void ConsumeIcecream(Icecream *icecream) {
ConsumeDrink(icecream);
ConsumeFood(icecream);
}
Surely it would be better in this case for Icecream
to just implement NutritionalConsumable
and provide a GetAsDrink()
and GetAsFood()
method that will return a proxy, purely for the sake of appearing as either food or drink. Otherwise that suggests that there is a method or object that accepts a Food
but somehow wants to later see it as a Drink
, which can only be achieved with a dynamic_cast
, and needn't be the case with a more appropriate design.
The usual C++ idiom is:
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 :
interface
: implementation
--public--> interface
--public--> interface_base
implementation_base
: implementation
--private--> implementation_base
--public--> interface_base
When client code does one of these derived to base conversions:
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.
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.