C++虚继承的古怪行为

来源:互联网 发布:java时间字符串转整形 编辑:程序博客网 时间:2024/05/22 13:14
    在使用菱形继承的时候,虚继承是一种非常管用的办法,可以避免从同一个祖先继承来两份数据。但是从实践看来,虚继承并不仅仅是这一点区别于普通的继承。
    虚继承对基类的构造函数的调用和一般的继承是不一样的,而且这种问题相当难发现。我们可以看看下边这个例子:
    class Base()    {        Base(int i)    }    class Son() : virtual public Base    {        Son(int i) : Base(i)        {        }    }    class Grandson() : virtual public Son    {        GrandSon(int i) : Son(i)        {        }    }


    这个例子看起来很正常,除了所有的构造函数都带有一个参数以外。当然这也没什么稀奇的,如果确实有这样的需求的话。
    但是,这样的code甚至不能编译通过,会报告Grandson没有缺省的构造函数Base::Base能够调用,除非你的Base类有一个无参数的构造函数。但坑爹的是,万一你有这样一个构造函数,就真的麻烦大了。GrandSon(int i)会自己调用Base(),而不是通过Son(int i)去调用Base(int i)。
    如果不使用虚继承,那么这样的code一切正常,一点问题都没有。
    随便把一个方法定义成“虚函数”一般不会有什么大问题,只不过是vtable会多占一些内存而已。但是把普通的继承改成虚继承就会遇到大麻烦。
    好吧,谁能告诉我,如果我需要菱形继承,还需要调用基类的相应构造函数,难道只能把构造函数写成这样!?
    GrandSon(int i) : Base(i), Son(i)
    这要是继承关系复杂一点,就太难维护了。

    我实在是没有想通为什么虚继承会有这样古怪的行为。这想必应该不是一个疏漏,而是故意为之。但是,其中的原因何在,我们又应该如何理解这种情况?希望有高手答疑解惑。
    
    下面是vs2012的一个console程序,输出结果极度坑爹。
    
class Base{public:    Base(int i)    {        std::cout << "Base constructor int" << std::endl;    }    Base()    {        std::cout << "Base constructor void" << std::endl;    }};class Son : virtual public Base{public:    Son(int i) : Base(i)    {        std::cout << "Son constructor int" << std::endl;    }    Son() : Base()    {        std::cout << "Son constructor void" << std::endl;    }};class Grandson : virtual public Son{public:    Grandson(int i) : Son(i)    {        std::cout << "Grand son constructor" << std::endl;    }};int _tmain(int argc, _TCHAR* argv[]){    Grandson someone(1);    return 0;}

它的运行结果如图1,看code很难想象居然会是这种结果。但如果不用虚继承,它的结果就会如我们所料,如图2.其中的区别仅仅是把那两个virtual.

图1,虚继承的执行结果

图2,普通继承的执行结果


0 0
原创粉丝点击