《C++ Primer》中提到在以下三种情况下需要使用构造函数初始化列表:
1. 需要初始化的类的成员变量是对象的情况;
2. 需要初始化的类的成员变量由const修饰的或初始化的类的引用成员变量;
3. 子类初始化父类的成员;
情况1:类的成员变量是对象,并且这个对象只有含参数的构造函数,没有无参数的构造函数;
如果我们有一个类的成员变量,它本身是一个类的对象,而且这个成员变量需要带参数的构造函数进行初始化,这时要对这个类的成员变量进行初始化,就必须调用这个类的成员变量的带参数的构造函数,如果没有初始化列表,那么将无法完成这一步,出现报错。
例如:
#include "iostream"
using namespace std;
class Test
{
public:
Test (int, int, int){
cout <<"Test" << endl;
};
private:
int x;
int y;
int z;
};
class Mytest
{
public:
Mytest():test(1,2,3){ //初始化
cout << "Mytest" << endl;
};
private:
Test test; //声明
};
int main(int argc, _TCHAR* argv[])
{
Mytest test;
return 0;
}
因为Test有了显示的带参数的构造函数,那么它是无法依靠编译器生成无参构造函数的,所以没有三个int型数据,就无法创建Test的对象。Test类对象是MyTest的成员,想要初始化这个对象test,那就只能用成员初始化列表,没有其它办法将参数传递给Test类构造函数。
情况2:需要初始化的类的成员变量由const修饰的或初始化的类的引用成员变量
例如:
class Test
{
priate:
const int a; //const成员声明
public:
Test():a(10){} //初始化
};
或
class Test
{
private:
int &a; //声明
public:
Test(int _a):a(_a){} //初始化
}
情况3:子类初始化父类的成员变量,需要在(并且也只能在)参数初始化列表中显示调用父类的构造函数
class Test
{
public:
Test(){};
Test (int x){ int_x = x;};
void show(){cout<< int_x << endl;}
private:
int int_x;
};
class Mytest:public Test
{
public:
Mytest() :Test(110) //调用父类的构造函数
{
//Test(110); // 构造函数只能在初始化列表中被显示调用,不能在构造函数内部被显示调用
};
};
int main()
{
Test *p = new Mytest();
p->show();
return 0;
}
执行顺序:
在对象构建过程中,如果有构造函数初始化列表,首先执行初始化列表中的内容,然后执行构造函数。并且,初始化列表中数据的构造顺序并不是按照在初始化列表中的先后顺序进行的,而是根据初始化列表中数据所在当前类中的定义顺序决定的。
例如:
class A
{
public:
A(int _a)
{
a = _a;
}
private:
int a;
};
class B
{
public:
B(int _b)
{
b = _b;
}
private:
int b;
};
class C
{
public:
C(int _c, int _a, int _b):v_b(_b),v_a(_a)
{
c = _c;
}
private:
int c;
A v_a;
B v_b;
};
上面的代码,v_a先于v_b定义,因此在构造函数初始化列表中先构造v_a,再构造v_b。