class basetwo {
private:
bool checkout() const {};
};
class derived :
public baseone,
public basetwo
{
};
int main(int argc, char** argv)
{
derived d;
d.checkout();
return 0;
}
在g++中编译上面代码,会提示error: request for member 'checkout' is ambiguous,表示在调用checkout时产生了歧义。
虽然checkout在两个基类中分别是public和private的,但是,在C++中,是先确认最佳匹配函数后再检验其可取用性,由于本例中两个checkout具有相同的匹配程度,因此,编译器也就没有检查可取用性。
需要注意的是:如果上面代码中注释掉d.checkout();这行,则编译运行都没问题。
为了解决歧义,你必须明白指出你要调用哪一个base class内的函数:
d.baseone::checkout(); //OK
上面的程序还不算悲剧,主角马上要粉墨登场了。在前面的多重继承中,如果baseone和basetwo又有一个更高级的共同base class,那恭喜你菱形灾难来了:
class base {...};
class baseone : public base {...};
class basetwo : public base {...};
class derived :
public baseone,
public basetwo
{...};
上面的代码中,对于base中的任何成员,在derived对象中都有两条访问路径,如果访问之,则g++编译时就会报告二义性错误。为了解决这个问题,可以令带有数据的base成为virtual base class,即令所有直接继承base的class采用virtual继承: class base {...}; class baseone : virtual public base {...}; class basetwo : virtual public base {...}; class derived : public baseone, public basetwo {...};
其实也不必谈虎色变,C++标准库中就内含一个多重继承体系,名称分别是basic_ios,basic_istream,basic_ostream和basic_iostream。
既然如此,那么我是否可以将所有使用public继承的地方都替换成virtual public继承,以避免潜在的二义性呢?当然可以,但是你会为此付出代价的,记住天下没有免费的午餐。
为了避免继承的来的成员变量重复,编译器必须提供若干幕后戏法,当然具体细节因编译器不同而异,但其后果一般是:使用virtual继承的那些class所产生的对象往往比使用non-virtual继承的兄弟们体积大,访问virtual base class的成员变量时,也比访问non-virtual base class的成员变量速度慢。
此外,virtual继承的成本还包括其它方面,比如:支配"virtual base class初始化"的规则比起non-virtual base的情况远为复杂且不直观。virtual base的初始化责任是由继承体系中最低层(most derived)class负责,这意味着:
(1)class若派生自virtual base而需要初始化,必须认知其virtual base——不论那些base距离多远。
(2)当一个新的derived class加入继承体系中,它必须承担其virtual base(不论直接或间接)的初始化责任。
对于virtual base class(即virtual继承),最后遵循如下规则:
(1)非必要不使用virtual base,平常请使用non-virtual继承。
(2)如果必须使用virtual base class,尽可能避免在其中放置数据。
当然也不是说多重继承一无是处了,世间万物都有其生存之道。当你需要“public继承某个interface class”和“private继承某个协助实现的class(或者说复合无法满足你的需求,比如:重写虚函数、访问base class的protected成员变量、空白基类最优化等)”的两相组合时,多重继承时来运转了。