一直不怎么在意构造函数的初始化列表,从觉得放在列表里和放在构造函数体里面一样,实际上 二者还是有差别的。
构造函数初始化列表以一个冒号开始,接着用逗号隔开各个成员,每个成员后面跟着一个放在小括号内的初始化式。例如下面的初始化列表
class Base {
public:
Base(int a, double b) :i_val(a), d_val(b)//(1)这个就是有初始化列表的构造函数
{cout << "Base(int a, double b)" << endl;}
Base() {i_val = 0; d_val = 0.0; cout << "Base()" << endl; }//(2)没有初始化列表
private:
int i_val;
double d_val;
}
实际上,构造函数有两个执行阶段:(1)初始化阶段 (2)普通计算阶段。计算阶段由构造函数体中的所有语句组成。不管成员是否在初始化列表中显示初始化,类类型的数据成员总是在初始化阶段初始化。初始化阶段发生在计算阶段开始之前。对于内置类型(比如说int)或者指针,那么在列表中和函数体中初始化是没有性能的差别.但是对于用户自定义类型,性能还是有差别的,因为构造函数会在进入函数体之前,对成员变量调用默认构造函数进行初始化。示例如下:
#include <iostream>
using namespace std;
class Base {
public:
Base() {cout << "Base()" << endl;}
Base(int j) {cout << j << endl;}
};
class Derive : public Base {
public:
//Derive(): b_val(2) { cout << "Derive()" << endl;} //(1)
Derive() { b_val = 2; cout << "Derive()" << endl;} //(2)
private:
Base b_val;
};
int main()
{
Base b;
Derive d;
}
把构造函数(1)注释掉, 保留(2),输出为
Base() //b的构造函数
Base() //Derive继承Base 调用Base构造函数生成父类
Base() //初始化列表中初始化b_val 调用的默认构造函数
2 //在函数体中调用Base的一个构造函数
Derive() //d的构造函数
把构造函数(2)注释掉, 保留(1),输出为
Base() //b的构造函数
Base() //Derive继承Base 调用Base构造函数生成父类
2 //在函数体中调用Base的一个构造函数
Derive() //d的构造函数
可以看到,b_val放在初始化列表中,可以少调用一次其默认构造函数(Base()),因为在进入函数体之前,已经初始化了b_val,然后在函数体里面又初始化了一次。
还有三种情况,成员变量必须放在初始化列表中进行初始化
1,没有默认构造函数的类类型成员。初始化列表会自动调用默认的构造函数,如果没有默认构造函数,编译出错
2,const 成员 const 对象或引用只能初始化,不能被赋值,所以唯一的合法机会就是放在初始化列表中进行初始化。
3,引用类型成员 同2