在使用菱形继承的时候,虚继承是一种非常管用的办法,可以避免从同一个祖先继承来两份数据。但是从实践看来,虚继承并不仅仅是这一点区别于普通的继承。
虚继承对基类的构造函数的调用和一般的继承是不一样的,而且这种问题相当难发现。我们可以看看下边这个例子:
这个例子看起来很正常,除了所有的构造函数都带有一个参数以外。当然这也没什么稀奇的,如果确实有这样的需求的话。
但是,这样的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程序,输出结果极度坑爹。
它的运行结果如图1,看code很难想象居然会是这种结果。但如果不用虚继承,它的结果就会如我们所料,如图2.其中的区别仅仅是把那两个virtual.
虚继承对基类的构造函数的调用和一般的继承是不一样的,而且这种问题相当难发现。我们可以看看下边这个例子:
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,普通继承的执行结果