我创建了一个钻石继承问题。看起来像这样
我认为我对虚拟继承的理解相当不错,但我现在认为我有点误解了它。
据我所知,虚拟继承告诉编译器忽略任何成员数据或函数,它们出现两次与钻石继承模式相同的名称,因此只有"非虚拟&#34 ;继承的组件将包含在派生类中。
但是我现在认为对编译器如何实现继承的理解是错误的。
我的继承层次结构中有2个菱形继承模式。它们使用包含的注释进行标记。
我还添加了一些注释,以显示我试图放置virtual
以解决编译器错误的位置,但是产生了不同的编译器错误。该说明简要描述了问题所在。 (如果您有兴趣,请参阅此问题的最后部分)
预期用途是创建std::list<GUIObject*>
。所有gui对象都应该能够Draw
和ProcessEvent
。并非所有gui对象都包含SingleLineBuffer
中包含的容器。
Buffer
和FileBuffer
继承自SingleLineBuffer
,以更改SingleLineBuffer
内容器的行为方式。 (FileBuffer
实际上只添加了新的文件IO功能。)
可以创建一个缓冲区的实例,但是我不在我正在使用的上下文中。
无法创建任何抽象GUI*
类的实例。考虑到这一点,GUIMultilineTextEntry
下面应该有一个额外的抽象基类,它继承自FileBuffer
。
用户可以创建实例的实际对象是Label
,Inputbox
和Textbox
。我打算将来添加更多内容,例如多行标签。这必须从可能继承自Buffer
和GUITextObject
的基类继承。
这种继承结构很快变得非常复杂。在我按照我的代码指示我做的事情的指导下,我一边写着它。例如,我写了一个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";
Size
在SingleLineBuffer
中定义。它是一个非虚函数,因为容器仅存在于SingleLineBuffer
中,因此编写Size
以便Buffer
和FileBuffer
正常工作。
virtual
放在GUITextObject
和GUITextEntry
之间,以阻止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
{ ...
Inputbox
和Textbox
需要同样的解决方案。
然而,这会导致进一步的错误
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
解决此问题,但我不确定这是一个好主意,因为我听说动态转换可能会显着减慢代码。
我通过在Buffer
和SingleLineBuffer
之间进行虚拟继承,再次尝试解决问题。 (参见红点)但是当我这样做时,编译器错误变为
error: no unique final overrider for ‘virtual void SingleLineBuffer::SetText(const string&)’ in ‘Textbox’
我的猜测这相当于编译器告诉我&#34;你通过继承覆盖了Buffer中的一些函数,但是你虚拟地继承了,你重写的函数也通过非虚继承存在于派生类中,所以我不知道哪一个应该采取预防措施&#34; - 但这确实是猜测。
由于这是一个相当长的问题,我不会在此列出该尝试的所有细节。
答案 0 :(得分:0)
名义问题的答案是no。 (通常MCVE会出现问题,但我想这里确实是一个答案。)至于详细问题:
virtual
,如果直接来自共同的祖先(&#34;钻石的顶部&#34;),您只需要一个副本完整的对象。 (所以在这里,你需要4 virtual
s,只需计算会聚箭头。)static_cast
(或通过,如其所说)虚拟基础,因为类布局因实例而异(因为其他基类不同)。但是,每个GUI操作一个dynamic_cast
的成本肯定是无法估量的。virtual
。