我正在尝试为游戏框架提出抽象,一种方法是创建一个图形和音频类,这些是您的游戏使用的接口,并为您的目标派生特定的实现平台(桌面/移动/控制台)。
我在这里有一些示例代码:
#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();
}
问题是输出是使用Text :: Print而不是IosText :: Print,我想知道这是否与钻石问题有关,我可能不得不使用虚拟继承等。非常感谢任何帮助。
答案 0 :(得分:2)
通常,不惜一切代价避免多次实现继承。在您的情况下,IosOutput
中有Graphics
和Text
两个副本,这会导致问题。
但是,最好的解决方案是根本不使用继承,而是使用成员身份 - IosOutput
具有IosGraphics
和IosText
类型的成员,并且这些成员可以合法地继承更抽象的Graphics
和Text
。
另外,请考虑接口 - 仅使用纯虚方法的类作为替代解决方案。
答案 1 :(得分:2)
“钻石问题”不是问题,它是不理解虚拟和非虚拟继承之间区别的症状。在上面的代码中,类Output
有两个类型为Graphics
的基类,一个来自Output
,另一个来自IosGraphics
。它还有两个类型为Text
的基类,一个来自Output
,另一个来自IosText
。因此,output->Print("Hello, World!)
会在其基础中调用Print()
的实现,即调用Text::Print()
。它对IosGraphics::Print()
没有任何了解。
如果您将IosGraphics
更改为Graphics
作为虚拟基础,并将IosText
更改为Text
作为虚拟基础,并将Output
更改为Graphics
和Text
作为虚拟基础,然后由于,事情会做你想做的事情优势规则。 Output
不会覆盖Rect()
,IosGraphics
会覆盖Output->Rect()
,因此IosGraphics::Rect()
的虚拟呼叫转移到Text::Print()
,{{1}}也是如此。
我知道,这听起来像魔术。规则有点奇怪,但它有效。试试吧。