在下面的代码中,我收到以下警告和错误:
test.cpp:15: warning: direct base 'B' inaccessible in 'D' due to ambiguity
test.cpp:15: error: no unique final overrider for 'virtual void A::f()' in 'D'
但是如果我从A中移除B的虚拟继承(即struct B : public A
),我只会得到警告,没有错误。
struct A
{
virtual void f() = 0;
};
struct B : public virtual A
{
void f() {}
};
class C : public B
{};
struct D : public C, virtual B
{};
int main()
{
return 0;
}
为什么呢?这是可怕的钻石吗?
答案 0 :(得分:3)
这是因为C
以非虚拟方式从B
继承,而D
以虚拟方式从B
继承。这会为您B
两次,包括两次f()
。
在B
中尝试C
的虚拟继承。
更新:那么为什么从B
移除A
中的虚拟继承时它会起作用?因为它改变了“最后的覆盖”。如果B
中的A
和C
中的B
中没有虚拟,则会A
两次:C
一次(最后覆盖f()
中的B
和B
中的虚拟D
中的f()
(B
中的B
的最后覆盖)。如果您将A
中的虚拟继承添加回A
,则f()
将仅出现一次,并且将有两个最终覆盖竞争从{{实现纯A
1}},B
,一次来自C
,一次来自虚拟B
。
作为一种变通方法,您可以向D添加using
,即using C::f;
或using B::f
。
参见C ++ 10.3 / 2
答案 1 :(得分:2)
让我们看看10.3[class.virtual]/2
类对象
的成员函数C::vf
的虚拟成员函数S
是最终覆盖,除非S
是基类子对象(如果有)的最派生类声明或继承另一个覆盖vf
。在派生类中,如果基类子对象的虚拟成员函数具有多个最终覆盖,则程序格式不正确。
使用A的虚拟继承,只有一个类型为A的基类子对象,其虚拟成员函数f()具有多个最终覆盖(在B类的每个子对象中有一个)
如果没有来自A的虚拟继承,则有两个不同类型A的基类子对象,它们的虚拟成员函数f()每个都有自己的最终覆盖(每个B子对象中有一个)
答案 2 :(得分:0)
虚拟基础子对象在完整对象中的所有基础子对象之间“共享”。由于A是在D :: C :: B和D :: B之间共享,因此无法确定哪个B对象应该将其f()
作为A::f().
的覆盖
考虑:
#include <iostream>
struct A {
virtual void f() = 0;
virtual ~A() {}
};
struct B : virtual A
{
void f() { std::cout << "B\n"; }
};
struct C : virtual A
{
void f() { std::cout << "C\n"; }
};
struct D : C, B {};
int main() {
D d;
A *a = dynamic_cast<A*>(&d); // single shared A between B and C
a->f(); // Should B::f() be called, or C::f()?
}
D中的B和C基础子对象共享相同的A基础子对象。当我们调用A :: f()时,将对覆盖函数进行虚拟查找。但是B和C都试图覆盖它,那么哪一个“赢”? x->f()
是否打印“B”或“C”?答案是,进入这种情况的程序是不正确的。
当我们通过使B和C继承非虚拟来消除共享时,单独的A个基础子对象的每个子对象都被唯一的基类覆盖:
#include <iostream>
struct A {
virtual void f() = 0;
virtual ~A() {}
};
struct B : A
{
void f() { std::cout << "B\n"; }
};
struct C : A
{
void f() { std::cout << "C\n"; }
};
struct D : C, B {};
int main() {
D d;
// two different A objects
A *a1 = static_cast<A*>(static_cast<B*>(&d));
A *a2 = static_cast<A*>(static_cast<C*>(&d));
a1->f();
a2->f();
}