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