我知道在提出这个问题之前,这里已经介绍了虚拟继承,在提出这个问题之前,我详细介绍了虚拟继承,并详细介绍了类似问题的详细信息,如下所示:
多重钻石继承编译无需虚拟但不使用 和 为什么 GCC 给我一个错误 - 最终重写器
我的问题略有不同,因为我没有使用纯虚函数,而是显式使用虚拟继承来拥有一个唯一的
base
类。层次结构如下:
base
/\
/ \
der1 der2
\ /
der3
我知道派生问题上的可怕钻石,这就是我使用虚拟继承的原因。
#include <iostream>
class base
{
public :
base()
{
std::cout<<"base()" << std::endl;
}
virtual void fun()
{
std::cout<<"base" << std::endl;
}
};
class der1: virtual public base
{
public :
void fun()
{
std::cout<<"der1" << std::endl;
}
};
class der2 : virtual public base
{
public :
void fun()
{
std::cout<<"der2" << std::endl;
}
};
class der3 : public der1,public der2
{
public :
/*void fun()
{
std::cout<<"der3" << std::endl;
}*/
//if I took out the comment and the function
//is defined it compiles fine as expected
};
int main()
{
base *p=new der3;
//used scope operation explicitly to avoid ambiguity
p->base::fun(); //here it complains about 'no unique final overrider for fun'
return 0;
}
我的理解是,由于我使用的是虚拟继承,因此应该只有一个
base,
实例,并且使用作用域运算符,我可以毫不含糊地调用虚拟 fun
函数。该函数不是纯虚函数。如果我确实在 der3
类上留下实现,则会出现编译器错误:
错误:‘der3’中的‘virtual void base::fun()’没有唯一的最终覆盖
我可以看到这个问题是如何工作的(最终覆盖者)。但我的没有。
base::fun
、der1::fun
和der2::fun
之间是否混淆了?作用域运算符有什么帮助吗?
任何线索或帮助表示赞赏。我正在使用 g++ 4.6.3。
最派生的类必须提供虚拟基类中虚拟函数的实现 - 否则它如何提供基类接口,因为中间类(即您的
der1
和 der2
)已经提供了两种替代方案- 它应该调用哪一个?您必须消除这种情况的歧义(即使用 der3::fun()
)。
当然,您实际上并没有调用
der3::fun()
,因为您明确请求 base::fun()
,但这并不意味着规则不适用,就像认为如果不尝试就可以实例化抽象类一样调用纯虚函数......在代码解决这些松散的问题之前,程序的格式是错误的。
使用作用域解析运算符指定要调用
base::fun
不会使错误消失,因为即使 main()
为空,程序也会出现格式错误。您根本不允许出现这样一种情况:虚拟函数在程序中存在的任何派生类中具有多个最终重写器。
非正式地说,仅仅因为尝试调用
p->fun()
would 是不明确的,即使你不这样做,程序也是不正确的。
注意:这与重载函数的情况形成鲜明对比,在重载函数中,潜在的歧义是允许的——甚至可能是不可避免的——只要你避免调用实际上是歧义的。为什么规则不同?基本上,这是因为即使构造一个
der3
类型的对象也无法以合理的方式完成——vtable 应该指向哪个版本的 fun
?
因为你有虚拟继承,
der3
类型的对象只会继承base
的一个实例,也就是说,它不会像没有虚拟继承那样拥有两个base
的副本。这意味着 der3
只能有一种 fun()
方法。如果 fun()
方法不是虚拟的,那么您将拥有该方法的副本。
由于在您的情况下只能有一种
fun()
方法,因此继承两个版本会导致歧义。您要么必须删除 der1
或 der2
覆盖 fun()
之一,才能解决歧义(然后 der3
将继承剩下的那个),或者您必须提供 der3
'自己的版本。