Adam Dreaver
Adam Dreaver

Reputation: 341

Trouble with multiple inheritance

I'm trying to come up with an abstraction for a game framework and one approach is to create, for example, a graphics and audio class, these are the interfaces used by your games, and you derive specific implementations for your target platforms ( desktop/mobile/console ).

I have some example code of the idea here:

#include <iostream>
#include <string>

using namespace std;

struct Graphics
{
    virtual ~Graphics() {}
    virtual void Rect() {}
};

struct Text
{
    virtual ~Text() {}
    virtual void Print(string s) {}
};

struct IosGraphics : public Graphics
{
    void Rect() { cout << "[]"; }
};

struct IosText : public Text
{
    void Print(string s) { cout << s << endl; }
};

struct Output : public Graphics, public Text
{
};

struct IosOutput : public Output, public IosGraphics, public IosText
{
};

int main() 
{ 
    Output * output = new IosOutput();
    output->Rect(); // this calling Graphics::Rect not IosGraphics::Rect
    output->Print("Hello World!"); // this calling Text::Print not IosText::Print
    cin.get();
}

The problem is that output is using the Text::Print instead of IosText::Print, I wonder if this is related to the diamond problem and I might have to use virtual inheritance or something. Any help is greatly appreciated.

Upvotes: 0

Views: 79

Answers (2)

Pete Becker
Pete Becker

Reputation: 76315

"The diamond problem" isn't a problem, it's a symptom of not understanding the distinction between virtual and non-virtual inheritance. In the code above, the class Output has two base classes of type Graphics, one from Output and one from IosGraphics. It also has two base classes of type Text, one from Output and one from IosText. So output->Print("Hello, World!) calls the implementation of Print() in its base, that is, it calls Text::Print(). It doesn't know anything about IosGraphics::Print().

If you change IosGraphics to have Graphics as a virtual base, and change IosText to have Text as a virtual base, and change Output to have Graphics and Text as virtual bases, then things will do what you want because of the Dominance rule. Output doesn't override Rect() but IosGraphics does, so virtual calls to Output->Rect() go to IosGraphics::Rect(), and similarly for Text::Print().

I know, that sounds like magic. The rule is a bit weird, but it works. Try it.

Upvotes: 2

epsalon
epsalon

Reputation: 2294

In general, avoid multiple implementation inheritance at all costs. In your case, IosOutput has two copies of Graphics and Text in it, which is causing the problem.

The best solution is, however, not to use inheritance at all, but instead use membership -- IosOutput has members of type IosGraphics and IosText, and those may legitimately inherit from a more abstract Graphics and Text.

Also, consider interfaces -- classes with only pure virtual methods as an alternative solution.

Upvotes: 2

Related Questions