在说构造函数之前我们得先弄明白几个问题,首先是什么是类的构造函数,什么是类的成员对象,什么是基类,然后我们再来说构造函数的调用顺序。
1、 类的构造函数
构造函数的功能主要用于在类的对象创建时定义初始化的状态。它没有返回值,也不能用void来修饰,这就保证了它不仅什么也不用自动返回,而且根本不能有任何选择。构造函数不能被直接调用,必须通过new运算符在创建对象时才会自动调用,一般方法在程序执行到它的时候被调用。一个类可以有多个构造函数,根据其参数个数的不同或参数类型的不同来区分它们,即构造函数的重载。
2、 类的成员对象
类包含数据成员和成员函数。当类中的成员数据是另一个类的对象时,我们就说这个类是该类的成员对象。
3、 基类
通过继承机制,可以利用已有的数据类型来定义新的数据类型。所定义的新的数据类型不仅拥有新定义的成员,而且还同时拥有旧的成员。我们称已存在的用来派生新类的类为基类,又称为父类。
4、 构造函数的调用顺序
我们来看下面一段代码:
class B1
{
public:
B1(int i) {cout<<"constructing B1 "<<i<<endl;}
};
class B2
{
public:
B2(int j) {cout<<"constructing B2 "<<j<<endl;}
};
class B3
{
public:
B3( ){cout<<"constructing B3 *"<<endl;}
};
class C: public B2, public B1, public B3
{
public:
C(int a, int b, int c, int d):B1(a),memberB2(d),memberB1(c),B2(b){}
private:
B1 memberB1;
B2 memberB2;
B3 memberB3;
};
void main( )
{ C obj(1,2,3,4); }
运行后的结果如下:
constructing B2 2
constructing B1 1
constructing B3 *
constructing B1 3
constructing B2 4
constructing B3 *
为什么会有以上的结果?
众所周知构造函数的执行次序如下:
调用基类构造函数,调用顺序按照他们的继承时声明的顺序。
调用内嵌成员对象的构造函数,调用顺序按照他们在类中声明的顺序。
派生类的构造函数体中的内容。
析构函数的调用顺序相反。
那么再来看以上的例子就很容易理解了。B2、B1、B3是C的基类,按照上述的顺序,我们先要构造基类,然后才是子对象,后是其本身的构造函数所以先要执行这三个类的构造函数。在构造时按照他们在类中的顺序,首先调用B2的构造函数
B2(int j) {cout<<"constructing B2 "<<j<<endl;}
由于在默认参数列表
C(int a, int b, int c, int d):B1(a),memberB2(d),memberB1(c),B2(b){}
中,将b的值传给了B2的构造函数,b为2,故打印出:
constructing B2 2
接下来要构造的是B1了。显然在C的默认参数构造列表中将a的值传给了B1,
所以打印出:
constructing B1 1
B3在构造时没有传递参数,调用B3( ){cout<<"constructing B3 *"<<endl;}
打印出:
cout<<"constructing B3 *
这时基类的构造函数已经执行完毕,接着该处理内嵌成员对象的构造函数了。
我们看到C类有三个对象:B1 memberB1;B2 memberB2;B3 memberB3;,按照构造函数的调用顺序,我们需要按照他们在类中声明的顺序来分别构造memberB1、memberB2、 memberB3。在默认的参数列表中,用c来构造了memberB1,用d来构造memberB2,
故打印出:
constructing B1 3
constructing B2 4
constructing B3 *
后调用本身的构造函数,由于函数体为空,故什么也没有打印出来。
总结下来,我们必须明确的是当一个类继承与基类,并且自身还包含有其他类的成员对象的时候,构造函数的调用顺序为:调用基类的构造函数->调用成员对象的构造函数->调用自身的构造函数。构造函数的调用次序完全不受构造函数初始化列表的表达式中的次序影响,与基类的声明次数和成员对象在函数中的声明次序有关。