bjs*_*123 11 c++ multiple-inheritance virtual-inheritance
我有类的钻石层次结构:
A
/ \
B C
\ /
D
Run Code Online (Sandbox Code Playgroud)
为了避免D中的两个A副本,我们需要在B和C使用虚拟继承.
class A { };
class B: virtual public A {};
class C: virtual public A { };
class D: public B, public C { };
Run Code Online (Sandbox Code Playgroud)
问题:为什么需要在B和C上执行虚拟继承,即使模糊度在D?如果它在D处会更直观.
为什么标准委员会将此功能设计为这样?如果B和C类来自第三方库,我们该怎么办?
编辑:我的回答是指示B和C类,只要它的派生对象被创建,它们就不应该调用A的构造函数,因为它将被D调用.
我不确定他们选择以这种方式设计虚拟继承的确切原因,但我相信其原因与对象布局有关.
假设C++的设计方式可以解决钻石问题,你实际上会在D中继承B和C,而不是在B和C中实际继承A.现在,B和C的对象布局是什么?好吧,如果没有人试图从它们中虚拟继承,那么它们每个都有自己的A副本,并且可以使用标准的优化布局,其中B和C各自在它们的基础上具有A. 但是,如果有人确实从B或C继承,那么对象布局必须不同,因为两者必须共享他们的A副本.
这个问题是,当编译器第一次看到B和C时,它无法知道是否有人将继承它们.因此,编译器必须依赖于虚拟继承中使用的较慢版本的继承,而不是默认情况下启用的更优化的继承版本.这违反了C++原则"不支付你不用的东西"(零开销原则),你只支付你明确使用的语言功能.
为什么需要在B和C上执行虚拟继承,即使模糊度在D?如果它在D处会更直观.
在您的示例中,B和C virtual专门用于请求编译器确保只涉及A的一个副本.如果他们没有这样做,他们就会有效地说"我需要自己的A基类,我不希望与任何其他派生对象共享它".这可能至关重要.
不想共享虚拟基类的示例
如果A是某种容器,B就是从它衍生而来并存储了一些特定类型的物体 - 比如"蝙蝠",而C则存储"猫".如果D期望B和C独立地提供关于蝙蝠和猫的数量的信息,如果C操作对蝙蝠做了什么,或者B操作对Cats做了什么,他们会非常惊讶.
想要共享虚拟基类的示例
假设D需要提供对A中某些函数或数据成员的访问,比如"A :: x"...如果A由B和C独立地(非虚拟地)继承,则编译器无法解析D :: x到B :: x或C :: x,程序员不必明确消除它的歧义.这意味着D不能用作A,尽管衍生链中隐含的只有两个"is-a"关系(即如果B"是"A,而D"是"B,则用户可以期望/需要使用D,好像D"是"A".
为什么标准委员会将此功能设计为这样?
virtual继承存在是因为它有时很有用.它由B和C指定,而不是D,因为它在B和C的设计方面是一个侵入性概念,并且还对B和C的封装,内存布局,构造和破坏以及函数调度有影响.
如果B和C类来自第三方库,我们该怎么办?
如果D需要从两者继承并提供对A的访问,但B和C不是设计为使用虚拟继承而且无法更改,则D必须负责将与A API匹配的任何请求转发到B和/或C和/或可选的另一个A它直接继承(如果它需要一个可见的"是A"关系).如果调用代码知道它正在处理D(即使是通过模板化),这可能是实用的,但是通过指向基类的指针对对象的操作将不知道D正在尝试执行的管理,并且整个事情可能是做对了很棘手.但这有点像说"如果我需要一个矢量而我只有一个列表","锯子而不是螺丝刀"......好吧,充分利用它或得到你真正需要的东西.
编辑:我的回答是指示B和C类,只要它的派生对象被创建,它们就不应该调用A的构造函数,因为它将被D调用.
这是一个重要的方面,是的.