我想帮助解释这个现象:
#include <iostream>
using namespace std;
class A
{
public:
void m() {cout<<"A::m "<<this<<endl;};
};
class B1: public A
{
public:
void m() {cout<<"B::m "<<this<<endl;};
};
class B2: public A ,public B1
{
};
class D : public B2
{};
int main()
{
B2 b;
D d;
A* a = &b; // Row 27
//error: a = &d; Base class 'A' is ambiguous // Row 28
return 0;
}
为什么Row27中的代码有效但Row28中的代码不起作用? 提前谢谢!
注意:我很清楚虚拟继承,我只是想知道Row27和Row28之间有什么区别 - 为什么在另一个没有时抛出编译错误?
答案 0 :(得分:1)
因为您使用非虚拟继承从A
派生,A
在类型D
的每个对象中间接创建两个类型为A
的子对象。在执行指向派生到指针到基础的转换时,编译器无法消除您引用哪个子对象的歧义,并发出错误。
为了只为D
类型的对象生成一个A
类型的子对象,您必须从继承路径D
虚拟继承A
1}}派生自class B1: virtual public A
{
// ...
};
class B2: virtual public A, public B1
{
};
:
B2
修改强>
我尝试在Visual Studio 2010 SP1上编译您的示例,该示例为我提供了关于class B2: public A, public B1
{
};
1>sotest.cpp(18): warning C4584: 'B2' : base-class 'A' is already a base-class of 'B1'
1> sotest.cpp(6) : see declaration of 'A'
1> sotest.cpp(11) : see declaration of 'B1'
定义的警告:
A
换句话说,出于某种原因,VC10似乎认为A* a = &b;
的继承是冗余的,忽略它。这就是作业class B2
编译的原因:A
实际上只从B1
继承一次(通过D
)。 D
也不是这样,因为VC10可能没有可忽略的冗余继承,A
有效地从B1
继承两次(通过B2
和{{1} })。
我忽略了VC10以这种方式运行的原因,我不知道是否有编译器选项可以抑制此行为。值得注意的是,GCC 4.7.2和Clang 3.2 拒绝来编译作业A* a = &b;
。
答案 1 :(得分:1)
对于未来的用户似乎:
我刚刚在GCC 4.7.1和Clang 3.2上尝试过它,除非我使用虚拟继承,否则我会遇到编译器错误。你用的是什么编译器? - 安迪普罗尔
VS2010确实可以编译,但发出一个警告,表明它忽略了B2对A的继承。这就是Row 27工作的原因。我认为这是VS2010的奇怪行为,我不知道是否有办法将其关闭(除了将所有警告视为错误) - Andy Prowl
谢谢@Andy Prowl。