多重继承和纯虚函数
以下代码:
struct interface_base
{
virtual void foo() = 0;
};
struct interface : public interface_base
{
virtual void bar() = 0;
};
struct implementation_base : public interface_base
{
void foo();
};
struct implementation : public implementation_base, public interface
{
void bar();
};
int main()
{
implementation x;
}
无法编译并出现以下错误:
test.cpp: In function 'int main()':
test.cpp:23:20: error: cannot declare variable 'x' to be of abstract type 'implementation'
test.cpp:16:8: note: because the following virtual functions are pure within 'implementation':
test.cpp:3:18: note: virtual void interface_base::foo()
我已经尝试过它并发现使 'interface -> “interface_base”和“implementation_base ->” interface_base'继承虚拟,解决了问题,但我不明白为什么。有人可以解释一下发生了什么事吗?
ps 我故意省略了虚拟析构函数以使代码更短。请不要告诉我把它们放进去,我已经知道了:)
The following code:
struct interface_base
{
virtual void foo() = 0;
};
struct interface : public interface_base
{
virtual void bar() = 0;
};
struct implementation_base : public interface_base
{
void foo();
};
struct implementation : public implementation_base, public interface
{
void bar();
};
int main()
{
implementation x;
}
fails to compile with the following errors:
test.cpp: In function 'int main()':
test.cpp:23:20: error: cannot declare variable 'x' to be of abstract type 'implementation'
test.cpp:16:8: note: because the following virtual functions are pure within 'implementation':
test.cpp:3:18: note: virtual void interface_base::foo()
I have played around with it and figured out that making the 'interface -> interface_base' and 'implementation_base -> interface_base' inheritances virtual, fixes the problem, but I don't understand why. Can someone please explain what is going on?
p.s. I omitted the virtual destructors on purpose to make the code shorter. Please don't tell me to put them in, I already know :)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您的继承树中有两个
interface_base
基类。这意味着您必须提供foo()
的两个实现。调用它们中的任何一个都会非常尴尬,需要多次转换才能消除歧义。这通常不是您想要的。要解决此问题,请使用虚拟继承:
使用虚拟继承,只会在所有虚拟提及的继承层次结构中创建相关基类的一个实例。因此,只有一个
foo()
,可以通过implementation_base::foo()
来满足。有关更多信息,请参阅之前的问题 - 答案提供了一些很好的图表让这一切变得更加清晰。
You have two
interface_base
base classes in your inheritance tree. This means you must provide two implementations offoo()
. And calling either of them will be really awkward, requiring multiple casts to disambiguate. This usually is not what you want.To resolve this, use virtual inheritance:
With virtual inheritance, only one instance of the base class in question is created in the inheritance heirarchy for all virtual mentions. Thus, there's only one
foo()
, which can be satisfied byimplementation_base::foo()
.For more information, see this prior question - the answers provide some nice diagrams to make this all more clear.
通常的 C++ 习惯用法是:
在这种情况下,我们将拥有:
在
implementation
中,唯一的interface_base
虚拟基是:interface
公开继承:实施
--公共-->接口
--public-->interface_base
implementation_base
私有继承:implementation
--private-->implementation_base
--public-->interface_base
当客户端代码执行以下派生到基类转换之一时:
重要的是从派生类到给定基类子对象至少有一个可访问的继承路径;其他不可访问的路径将被忽略。因为基类的继承在这里只是虚拟的,所以只有一个基类主题,所以这些转换永远不会含糊不清。
在这里,从
implementation
到interface_base
的转换始终可以由客户端代码通过interface
完成;另一条无法到达的路径根本不重要。 唯一的interface_base
虚拟基是从implementation
公开继承的。在许多情况下,实现类(
implementation
,< code>implementation_base)将对客户端代码隐藏:仅公开接口类(interface
、interface_base
)的指针或引用。The usual C++ idiom is:
In this case we would have:
In
implementation
, the uniqueinterface_base
virtual base is :interface
:implementation
--public-->interface
--public-->interface_base
implementation_base
:implementation
--private-->implementation_base
--public-->interface_base
When client code does one of these derived to base conversions:
what matters is only that there is a least one accessible inheritance path from the derived class to the given base class subobject; other inaccessible paths are simply ignored. Because inheritance of the base class is only virtual here, there is only one base class subject so these conversions are never ambiguous.
Here, the conversion from
implementation
tointerface_base
, can always be done by client code viainterface
; the other inaccessible path does not matter at all. The uniqueinterface_base
virtual base is publicly inherited fromimplementation
.In many cases, the implementation classes (
implementation
,implementation_base
) will be kept hidden from client code: only pointers or references to the interface classes (interface
,interface_base
) will be exposed.对于“解决”钻石继承问题的情况,bdonlan提供的解决方案是有效的。话虽如此,您可以避免设计中的钻石问题。为什么给定类的每个实例都必须被视为两个类?您是否打算将同一个对象传递给一个类似以下内容的类:
在这种情况下,Icecream 最好只实现 NutritionalConsumable 并提供一个
Icecream
>GetAsDrink() 和GetAsFood()
方法将返回代理,纯粹是为了显示为食物或饮料。否则,这表明有一个方法或对象接受Food
但不知何故希望稍后将其视为Drink
,这只能通过dynamic_cast 来实现
,并且不必是更合适的设计。For the case of 'solving' the diamond inheritance problem, the solutions offered by bdonlan are valid. Having said that, you can avoid the diamond-problem with design. Why must every instance of a given class be seen as both classes? Are you ever going to pass this same object to a class that says something like:
Surely it would be better in this case for
Icecream
to just implementNutritionalConsumable
and provide aGetAsDrink()
andGetAsFood()
method that will return a proxy, purely for the sake of appearing as either food or drink. Otherwise that suggests that there is a method or object that accepts aFood
but somehow wants to later see it as aDrink
, which can only be achieved with adynamic_cast
, and needn't be the case with a more appropriate design.