3、虚继承对构造函数的影响
对于构造函数的影响,借助于下面的原则可以理解(来自《深入理解C++对象模型》)
构造函数的调用可能内带大量的隐藏码,因为编译器会对构造函数进行扩充,一般而言编译器所作的扩充操作大约如下:
1、记录在成员初始化列表中的数据成员的初始化操作会被放到构造函数本身中,按照数据成员声明的顺序
2、如果有一个数据成员没有出现在初始化列表中,但是它有一个默认构造函数,那么这个默认构造函数会被调用
3、在那之前,如果有虚函数表,会调整虚函数表指针
4、在那之前,会对上一层基类的构造函数进行调用
5、在那之前,所有虚基类的构造函数必须被调用,按照声明的继承顺序从左往右,从最深到最浅的顺序
从上面的规则可以看出,对于虚基类的构造函数的调用是放在最前面的,并且需要子类对于虚基类的构造函数拥有访问权限
从下面的示例代码可以看出
#include<iostream>
using std::cout;
using std::endl;
class Base
{
protected:
int value;
public:
Base()
{
cout<<"in Base"<<endl;
}
};
class DerivedA:protected Base
{
protected:
int valueA;
public:
DerivedA()
{
cout<<"in DerivedA"<<endl;
}
};
class DerivedB
{
protected:
int valueB;
public:
DerivedB()
{
cout<<"in DerivedB"<<endl;
}
};
class TestClass
{
public:
TestClass()
{
cout<<"in TestClass"<<endl;
}
};
class MyClass:DerivedA,virtual DerivedB
{
private:
int my_value;
TestClass testData;
public:
MyClass()
{
//cout<<"in MyClass"<<value<<endl;
}
};
int main()
{
/*
Base base_obj;
DerivedA derA_obj;
DerivedB derB_obj;
MyClass my_obj;
cout<<"size of Base object "<<sizeof(base_obj)<<endl;
cout<<"size of DerivedA object "<<sizeof(derA_obj)<<endl;
cout<<"size of DerivedB object "<<sizeof(derB_obj)<<endl;
cout<<"size of MyClass object "<<sizeof(my_obj)<<endl;*/
MyClass my_obj;
}
虽然在声明继承顺序的时候DerivedA的顺序是在DerivedB的前面的,但是由于DerivedB是虚拟继承,所以对DerivedB的调用会在最前。但是如果将DerivedA继承也改成虚继承,那么调用顺序就会发生变化。并且具体DerivedA的构造函数的调用与DerivedB的构造函数的调用顺序还与是MyClass虚继承DerivedA还是DerivedA虚继承Base有关。还是用代码来说明
MyClass虚继承DerivedA的情况,由于DerivedA声明在DerivedB前面,并且都是虚继承,所以先调用DerivedA的构造函数。但DerivedA的父类是Base,所以具体的构造函数的调用顺序是Base、DerivedA、DerivedB、TestClass。这种情况代码如下
#include<iostream>
using std::cout;
using std::endl;
class Base
{
protected:
int value;
public:
Base()
{
cout<<"in Base"<<endl;
}
};
class DerivedA:protected Base
{
protected:
int valueA;
public:
DerivedA()
{
cout<<"in DerivedA"<<endl;
}
};
class DerivedB
{
protected:
int valueB;
public:
DerivedB()
{
cout<<"in DerivedB"<<endl;
}
};
class TestClass
{
public:
TestClass()
{
cout<<"in TestClass"<<endl;
}
};
class MyClass:virtual DerivedA,virtual DerivedB
{
private:
int my_value;
TestClass testData;
public:
MyClass()
{
//cout<<"in MyClass"<<value<<endl;
}
};
int main()
{
/*
Base base_obj;
DerivedA derA_obj;
DerivedB derB_obj;
MyClass my_obj;
cout<<"size of Base object "<<sizeof(base_obj)<<endl;
cout<<"size of DerivedA object "<<sizeof(derA_obj)<<endl;
cout<<"size of DerivedB object "<<sizeof(derB_obj)<<endl;
cout<<"size of MyClass object "<<sizeof(my_obj)<<endl;*/
MyClass my_obj;
}
输出如下
但如果将虚继承放在由DerivedA虚继承Base,而MyClass非虚继承DerivedA。那么按照从左往右,从深到浅的顺序,调用顺序应该是Base、DerivedB、DerivedA、TestClass
,代码示例如下
#include<iostream>
using std::cout;
using std::endl;
class Base
{
protected:
int value;
public:
Base()
{
cout<<"in Base"<<endl;
}
};
class DerivedA:protected virtual Base
{
protected:
int valueA;
public:
DerivedA()
{
cout<<"in DerivedA"<<endl;
}
};
class DerivedB
{
protected:
int valueB;
public:
DerivedB()
{
cout<<"in DerivedB"<<endl;
}
};
class TestClass
{
public:
TestClass()
{
cout<<"in TestClass"<<endl;
}
};
class MyClass: DerivedA,virtual DerivedB
{
private:
int my_value;
TestClass testData;
public:
MyClass()
{
//cout<<"in MyClass"<<value<<endl;
}
};
int main()
{
MyClass my_obj;
}
输出如下
这里还有一个问题是,左右顺序和深浅顺序该如何抉择呢?答案是先左右,再深浅。比如将上面的代码改为class MyClas: virutal DerivedB, DerivedA{...}
那么最后的调用顺序就是DerivedB,Base, DerivedA,TestClass。读者可以自己验证。
本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/2012-11/74492p5.htm