在钻石问题的求解中,为什么要虚拟地继承两次grand-parent类
in solution of diamond problem, why we need to inherit grand parent class two time virtually?
在下面的代码中,为什么我必须在类B
和C
中都继承类A
?
我了解到第一个编译器在派生类中找到目标函数。若并没有在其中找到,那个么编译器必须在它的基类中进行搜索。所以,如果我实际上只继承了一次(类A
(,那么编译器应该只通过一个路径找到目标函数(aa()
(,即下面的D
到C
到A
。
若我只继承了一次,那个么问题仍然存在。
#include <iostream.h>
using namespace std;
class A
{
public:
void aa() { cout<<"aa"<<endl; }
};
class B: public virtual A
{ };
class C: public /*virtual*/ A
{ };
class D: public C,public B
{ };
int main()
{
D d1;
d1.aa();
return 0;
}
我得到这个错误:
Error : Member is ambiguous: 'A::aa' and 'A::aa' in function `main()`
如果你想要另一条规则,就编另一种语言。如果你不喜欢任何编程语言中的规则,请尝试这样做,然后回复我们,告诉我们你想出了什么确切的语义。
但一个更深层次的答案是,虚拟污染非虚拟的东西毫无意义。
你是否希望一个非虚拟成员函数变成虚拟的,因为其他具有相同签名的函数在其他类中是虚拟的,并且它们最终是两个基?这是一个真实的问题,用一种想象中的语言,你可以根据自己的直觉来弥补:
struct A {
void f(); // non virtual!
};
struct AA : A {
void f(); // non virtual, hides, does not override
};
struct B {
virtual void f();
};
struct BB : B {
void f(); // virtual overrider
};
struct A {
void f(); // non virtual!
};
struct AABB : AA, BB {
// is AA::f() now virtual? what about A::f()?
};
如果你不希望BB
中的虚拟性会改变AA
的语义,那么你有一个大致的答案:编写虚拟性不会改变任何先前建立的虚拟性的虚拟性。
因此,你所要接受的是,虚性是继承的一种性质,是派生类和它的基之间的一对一关系。继承在任何给定的类中都被建立为虚拟或非虚拟。
这就是它的工作原理。来自cppreference:
虚拟基类
对于指定为虚拟的每个不同基类派生对象只包含一个该类型的基类子对象,即使类在继承层次结构中多次出现(只要它每次都是虚拟继承的(。
也许你的理解是"一旦继承了虚拟基类,它只作为子对象出现一次",但更正确的非正式方式是"所有虚拟继承的子对象都只作为单个子对象出现"。
考虑一下baseA
希望其base
出现在最终派生类中的人为情况。它将使用非虚拟继承:
struct baseA : base {};
现在,baseB
类希望其派生包含在所有其他虚拟继承基类之间共享的base
子对象,它将使用虚拟继承:
struct baseB : virtual base {};
现在我们可以从两者继承:
struct foo : baseA, baseB {};
如果虚拟继承按您预期的方式工作,这将破坏baseA
(假设foo
获得一个单独的base
子对象(。此外,添加baseB
继承会将baseA
的继承变成虚拟继承,这也是违反直觉的。
这实际上只是猜测和TL;DR实际上就是:这就是虚拟继承的定义。
包含的链接中有一个很好的解释。
https://isocpp.org/wiki/faq/multiple-inheritance
你需要向下滚动到关于"可怕的钻石"的部分。下面的部分解释了如何通过使用关键字"virtual"来处理可怕的钻石。
简而言之,当您有多个继承,并且您从中继承的某些东西共享一个共同的祖先时,您最终会得到基对象的两个副本。在你的例子中,你得到了A的两个副本——一个在B,一个在C。然后当从D使用它时,你的代码不知道你真正指的是哪一个。这可能不是你真正想要的。您只需要a的一个实例,并且希望B和C共享它
如果删除类C的virtual
关键字,则在类树中有第二个a实例。如果你在课堂上有两次A访问一个成员是ambiguous
!
如果您像这样扩展您的示例,您可以同时看到这两个实例并消除错误消息:
int instance_count = 0;
class A
{
int myInstance;
public:
A(): myInstance{ instance_count++ } { std::cout << "create instance # " << myInstance << std::endl;}
void aa() { std::cout<<"Instance"<< myInstance << std::endl; }
};
class B:public A
{ };
class C:public /*virtual*/ A
{ };
class D:public C,public B
{ };
int main()
{
D d1;
d1.::C::aa(); // you can access each instance by giving the path through the hirarchy
d1.::B::aa();
return 0;
}
- 继承函数的重载解析
- 继承期间显示未知行为的子类
- 头文件-继承c++
- 为什么在保护模式下继承升级不起作用
- 通过继承类使用来自不同命名空间的运算符
- 子目录是否继承属性,例如add_definitions,include_directories和父Cmakelist.t
- 混合组合和继承的C++问题
- 继承:构造函数,初始化C++11中基类的类C数组成员
- 从类继承时,继承的类是否会通过父类重新定义继承的变量
- 公共与私人继承
- 如何创建从同一类继承的不同对象的向量
- 如何从另一个文件继承私有成员变量和公共函数
- 在模板基类中为继承类中的可选重写生成虚拟方法
- 带有继承的C++工厂
- 我应该避免多重实现继承吗
- C++继承更改成员
- 从具有默认值的部分指定模板类继承时发生SWIG错误,具有不带默认值的正向声明
- 在钻石问题的求解中,为什么要虚拟地继承两次grand-parent类
- Sean Parent:对于继承层次结构中的多态类型,具有可变对象是极端的例外
- 如何在C++实现继承并解决错误"parent class is not accessible base of child class"?