现在来测试一下在多重继承,虚继承,MI继承中虚继承中构造函数的调用情况。
先来测试一些普通的多重继承。其实这个是显而易见的。 测试代码:
//测试多重继承中派生类的构造函数的调用顺序何时调用
//Fedora20 gcc version=4.8.2
#include
using namespace std;
class base
{
public:
base()
{
cout<<"base created!"<<endl;
}
~base()
{
cout<<"base destroyed!"<<endl;
}
};
//公有继承
class A:public base
{
public:
A()
{
cout<<”A created!”<<endl;
}
~A()
{
cout<<”A destroyed!”<<endl;
}
};
class B:public base
{
public:
B()
{
cout<<”B created!”<<endl;
}
~B()
{
cout<<”B destroyed!”<<endl;
}
};
class C:public B,public A//多重继承
//改为class C:public A,public B后,AB构造的顺序改变
{
public:
C(){
cout<<”C created!”<<endl;
}
~C(){
cout<<”C destroyed”<<endl;
}
};
int main()
{
C test;
}
测试结果: 显而易见,普通的多重基层依次调用上一层父类的构造函数。这就说明了MI有时候不太好的情况了,那就是如果你不小心的话,就会从上一层继承关系中意外创建多个基类的对象,例如如果base里面有一个string name属性,那么D创建的时候就会创建两个string name的对象,而这个,不一定是你想要的结果。 现在,来测试一下父类中存在虚继承的情况。 测试代码:
//测试多重继承中派生类的构造函数的调用顺序何时调用
//Fedora20 gcc version=4.8.2
#include
using namespace std;
class base
{
public:
base(){
cout<<”base created!”<<endl;
}
~base(){
cout<<”base destroyed!”<<endl;
}
};
//虚继承
class A:public virtual base
{
public:
A(){
cout<<”A created!”<<endl;
}
~A() {
cout<<”A destroyed!”<<endl;
}
};
//虚继承
class B:public virtual base
{
public:
B(){
cout<<”B created!”<<endl;
}
~B(){
cout<<”B destroyed!”<<endl;
}
};
//C是虚继承base
class C:public virtual base
{
public:
C(){
cout<<”C created!”<<endl;
}
~C(){
cout<<”C destroyed”<<endl;
}
};
class D:public A,public B,public C
{//D A,B,C都是虚继承的形式。
public:
D(){
cout<<”D created!”<<endl;
}
~D(){
cout<<”D destroyed!”<<endl;
}
};
int main()
{
D d;
}
测试结果: 可以看到,如果上一层继承中都是虚继承,那么,只会在最开始一次调用base基类构造函数。 那么,如果A不是虚继承呢?
//A不是虚继承
class A:public base
{
public:
A(){
cout<<”A created!”<<endl;
}
~A() {
cout<<”A destroyed!”<<endl;
}
};
测试结果: 看到虚继承的B,C依旧只是在最开始使调用了一次base,但是A类不再是虚继承,因此A类的构造函数也调用来一次base的构造函数. [admin@localhost csuper]$ ./c base created! //这是BC共同调用的base构造函数 base created! //这是调用A类的构造函数时,A类构造函数又调用了一次base的构造函数。 A created! 为了测试这一想法是否真是如此,这里我们利用控制变量法,仅使B类不是虚继承。
//仅令B不是虚继承,A,C依旧是虚继承
class B:public base
{
public:
B(){
cout<<”B created!”<<endl;
}
~B(){
cout<<”B destroyed!”<<endl;
}
};
结果: 可以看出,结果正是如此。 同时发现了一个有趣的情况。当A,B,C都是虚继承base的时候,D虚继承C,看看结果又会如何?
class D:public A,public B,virtual public C
{// A,B,C都是虚继承base的形式。
//D又虚继承C
public:
D(){
cout<<”D created!”<<endl;
}
~D(){
cout<<”D destroyed!”<<endl;
}
};
结果: 可以看到,构造D的对象时,先调用了base,然后就到了调用C的构造函数了,说明编译器在构造的时候,是优先构造虚继承的对象的,这样就保证了构造A,B对象的时候,如果AB是虚继承于base,就不会创建多个从base定义的成员属性了。 但是如果C不是虚继承base,但D又是虚继承C的时候又会如何呢?
//C不是虚继承base
class C:public base
{
public:
C(){
cout<<”C created!”<<endl;
}
~C(){
cout<<”C destroyed”<<endl;
}
};
class D:public A,public B,virtual public C
{//D A,B,C都是虚继承的形式。
public:
D(){
cout<<”D created!”<<endl;
}
~D(){
cout<<”D destroyed!”<<endl;
}
};
结果: 可以看出,第一个调用的base应该是属于A,B调用的,因此,其实上面的说法是不对的,因为如果是优先调用C的构造函数,输出应该是 base created! //如果是优先调用C C created! base created! //A,B调用的base应该在这里出现,但事实上却不是。 A created! B created! D created! ……….. 再看看下面的测试:
//测试多重继承中派生类的构造函数的调用顺序何时调用
//Fedora20 gcc version=4.8.2
#include
using namespace std;
class base
{
public:
base(){
cout<<”base created!”<<endl;
}
~base(){
cout<<”base destroyed!”<<endl;
}
};
//A是虚继承
class A:public virtual base
{
public:
A(){
cout<<”A created!”<<endl;
}
~A() {
cout<<”A destroyed!”<<endl;
}
};
//B不是虚继承
class B:public base
{
public:
B(){
cout<<”B created!”<<endl;
}
~B(){
cout<<”B destroyed!”<<endl;
}
};
//C不是虚继承base
class C:public base
{
public:
C(){
cout<<”C created!”<<endl;
}
~C(){
cout<<”C destroyed”<<endl;
}
};
class D:public A,public virtual B,virtual public C
{// B,C都是虚继承的形式。
public:
D(){
cout<<”D created!”<<endl;
}
~D(){
cout<<”D destroyed!”<<endl;
}
};
int main()
{
D d;
}
这里只有A是虚继承base,B,C都不是虚继承,但是在D里面,刚好相反,看看结果如何 说实在的,输出是这样我是没有想到的。 base created! //这个base是哪一个调用的呢? base created! B created! base created! C created! 后来我再这样测试一下我就知道是为什么了。 令A,B,C均不虚继承与base,但是D继承与A,并且虚继承B,C,看结果
因此可以看出,上一次的第一个base是因为A而调用的base,也就是说,编译器是先检测上一层继承关系(A,B,C)中,哪一个是虚继承于再上一层的类(base),如果有,则优先调用该虚继承与base的类(也就是A)的基类的(也就是base)构造函数,然后再调用D中虚继承的类的构造函数(B,C),最后才调用A自己的构造函数,这里有点复杂,还是上一次测试的例子: base created! //这个base是因为A是虚继承与base时调用的,但是调用完之后并不直接调用A (A created!)
base created! //这个是调用B时调用的base
B created!
base created! //这个是调用C时i调用的base
C created! A created! //最后才调用A created! 因此,可以发现,如果你不想该类(例如A)在被继承的时候防止派生类(D)多次创建该类(A)的基类(base)的多个对象,那么,就应该将该类声明为virtual 继承基类(base),vitual的作用是对下一层次的派生类起作用,对该类(A)并不起特别作用(但是该类(D)会优先调用虚继承类(B,C)的构造函数)。 —————————————————————————————————————————————————— //写的错误或者不好的地方请多多指导,可以在下面留言或者给我发邮件,指出我的错误以及不足,以便我修改,更好的分享给大家,谢谢。 转载请注明出处:https://www.royalchen.com/ author:royalchen Email:royalchen@royalchen.com ———————————————————————————————————————————————————
- 本文作者: royalchen
- 本文链接: http://www.royalchen.com/2016/02/24/fontcolorred置顶fontc学习笔记5,多重继承中派生类的构造函/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!