Chinaunix首页 | 论坛 | 博客
  • 博客访问: 6320713
  • 博文数量: 2759
  • 博客积分: 1021
  • 博客等级: 中士
  • 技术积分: 4091
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-11 14:14
文章分类

全部博文(2759)

文章存档

2019年(1)

2017年(84)

2016年(196)

2015年(204)

2014年(636)

2013年(1176)

2012年(463)

分类: C/C++

2013-07-06 09:34:26

原文地址:C++之菱形灾难 作者:scq2099yt

        在多重继承中,派生类可能会从一个以上的基类中继承相同名称的函数、typedef等,这样会导致歧义的产生,比如:
        #include
       
        class baseone {
        public:
                void checkout() {};
        };

        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成员变量、空白基类最优化等)”的两相组合时,多重继承时来运转了

                                
         



阅读(421) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~