我的钻石继承编译器错误无法解决吗?

时间:2018-06-02 10:09:53

标签: c++ inheritance virtual-inheritance diamond-problem

结构

我创建了一个钻石继承问题。看起来像这样

inheritance diagram

我认为我对虚拟继承的理解相当不错,但我现在认为我有点误解了它。

  • 据我所知,虚拟继承告诉编译器忽略任何成员数据或函数,它们出现两次与钻石继承模式相同的名称,因此只有"非虚拟&#34 ;继承的组件将包含在派生类中。

  • 但是我现在认为对编译器如何实现继承的理解是错误的。

我的继承层次结构中有2个菱形继承模式。它们使用包含的注释进行标记。

我还添加了一些注释,以显示我试图放置virtual以解决编译器错误的位置,但是产生了不同的编译器错误。该说明简要描述了问题所在。 (如果您有兴趣,请参阅此问题的最后部分)

使用率

预期用途是创建std::list<GUIObject*>。所有gui对象都应该能够DrawProcessEvent。并非所有gui对象都包含SingleLineBuffer中包含的容器。

BufferFileBuffer继承自SingleLineBuffer,以更改SingleLineBuffer内容器的行为方式。 (FileBuffer实际上只添加了新的文件IO功能。)

可以创建一个缓冲区的实例,但是我不在我正在使用的上下文中。

无法创建任何抽象GUI*类的实例。考虑到这一点,GUIMultilineTextEntry下面应该有一个额外的抽象基类,它继承自FileBuffer

用户可以创建实例的实际对象是LabelInputboxTextbox。我打算将来添加更多内容,例如多行标签。这必须从可能继承自BufferGUITextObject的基类继承。

这种继承结构很快变得非常复杂。在我按照我的代码指示我做的事情的指导下,我一边写着它。例如,我写了一个Textbox类,然后说&#34; Textbox中的容器与Label中的容器基本相同,因此它们应该从一个公共对象继承&#34;。不同之处在于文本框具有文件IO,另外还有一个继承步骤,文本框可以在容器中包含新行字符,因此这里也会指定一个额外的继承步骤。

问题

  • 可以解决此继承问题吗?

  • 哪些类应该从其他类中继承。

我的尝试

  • 无虚拟继承

编译错误:(多个版本)

error: request for member ‘Size’ is ambiguous
 status_text << "Save: " << static_cast<Textbox*>(current_window._guiobject_map_.at("textbox"))->GetFilename() << ", " << static_cast<Textbox*>(current_window._guiobject_map_.at("textbox"))->Size() << " bytes";

SizeSingleLineBuffer中定义。它是一个非虚函数,因为容器仅存在于SingleLineBuffer中,因此编写Size以便BufferFileBuffer正常工作。

  • 打破钻石1:将virtual放在GUITextObjectGUITextEntry之间,以阻止Size通过GUITextObject&#34;在缓冲区中覆盖它之前。 (蓝色标记)

编译器错误(1):

error: no matching function for call to ‘GUITextObject::GUITextObject()’

我可以通过从GUIMultilineTextEntry调用所需的构造函数来解决这个问题。我不明白为什么需要这样做。 (第二个问题)修复如下所示:

GUIMultilineTextEntry(const FontTextureManager& ftm, const int width, const int height)
    : GUITextEntry(ftm, width, height)
    , GUITextObject(ftm, width, height) // also call the constructor for the class which was inherited virtual in the previous step
{ ...

InputboxTextbox需要同样的解决方案。

然而,这会导致进一步的错误

error: cannot convert from pointer to base class ‘GUIObject’ to pointer to derived class ‘Textbox’ via virtual base ‘GUITextObject’
 static_cast<Textbox*>(current_window._guiobject_map_.at("textbox"))->SetFilename(filename);

我相信我可以使用dynamic_cast代替static_cast解决此问题,但我不确定这是一个好主意,因为我听说动态转换可能会显着减慢代码。

  • 打破钻石1,第二次尝试:

我通过在BufferSingleLineBuffer之间进行虚拟继承,再次尝试解决问题。 (参见红点)但是当我这样做时,编译器错误变为

error: no unique final overrider for ‘virtual void SingleLineBuffer::SetText(const string&)’ in ‘Textbox’

我的猜测这相当于编译器告诉我&#34;你通过继承覆盖了Buffer中的一些函数,但是你虚拟地继承了,你重写的函数也通过非虚继承存在于派生类中,所以我不知道哪一个应该采取预防措施&#34; - 但这确实是猜测。

  • 我尝试过类似的东西来破坏钻石2,但遇到了类似的编译器错误。

由于这是一个相当长的问题,我不会在此列出该尝试的所有细节。

1 个答案:

答案 0 :(得分:0)

名义问题的答案是no。 (通常MCVE会出现问题,但我想这里确实是一个答案。)至于详细问题:

  • 继承必须是virtual,如果直接来自共同的祖先(&#34;钻石的顶部&#34;),您只需要一个副本完整的对象。 (所以在这里,你需要4 virtual s,只需计算会聚箭头。)
  • 您需要在每个 concrete课程中调用每个虚拟基础的构造函数,因为most-derived class会直接初始化它们。
  • 您实际上无法使用static_cast(或通过,如其所说)虚拟基础,因为类布局因实例而异(因为其他基类不同)。但是,每个GUI操作一个dynamic_cast的成本肯定是无法估量的。
  • 您独特的最终覆盖者&#34;错误分析可能是正确的,但答案更多是 cowbell virtual