类对象的构造顺序是:
1.分配内存,调用构造函数时,隐式/显示的初始化各数据成员;
2.进入构造函数后在构造函数中执行一般赋值与计算。

使用初始化列表有两个原因:

原因1.必须这样做:

《C++ Primer》中提到在以下三种情况下需要使用初始化成员列表:

  1. 需要初始化的数据成员是对象的情况(这里包含了继承情况下,通过显示调用父类的构造函数对父类数据成员进行初始化);
  2. 需要初始化const修饰的类成员
  3. 需要初始化引用成员数据

1 的说明:数据成员是对象,并且这个对象只有含参数的构造函数,没有无参数的构造函数;

原因2.效率要求这样做:
类对象的构造顺序显示,进入构造函数体后,进行的是计算,是对成员变量的赋值操作,显然,赋值和初始化是不同的,这样就体现出了效率差异,如果不用成员初始化列表,那么类对自己的类成员分别进行的是一次隐式的默认构造函数的调用,和一次赋值操作符的调用,如果是类对象,这样做效率就得不到保障。

类成员是对象时的运行分析

不使用初始化列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class Base
{
public:
int i;
Base()
{
i = 0;
cout << i << '\t' << "Base无参构造函数" << endl;
}
Base(int tmp)
{
i = tmp;
cout << i << '\t' << "Base含参构造函数" << endl;
}
Base(const Base &tmp)
{
i = tmp.i;
cout << i << '\t' << "Base拷贝构造函数" << endl;
}
};

class Test
{
public:
int a;
Base b;

Test(int i, Base tmp)
{
a = i;
b = tmp;
cout<<a<<'\t' << "Test 构造函数" << endl;
}

~Test()
{
cout<<a<<'\t' << "Test析构函数"<<endl;
}
Test(const Test &b)
{
a = b.a;
cout << b.a <<'\t'<< "Test拷贝构造函数" << endl;
}
};
int main()
{
Base b;
Test a(10, b);
}

输出为

0 Base无参构造函数
0 Base拷贝构造函数
0 Base无参构造函数
10 Test 构造函数
10 Test析构函数

如果不使用初始化列表,Test对象初始化时,需要先调用一次无参构造参数,然后赋值运算,如果没有实现,将会报错。

使用初始化列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Test
{
public:
int a;
Base b;

Test(int i, Base tmp):a(i),b(tmp)
{
cout << a << '\t' << "Test 构造函数" << endl;
}

~Test()
{
cout<<a<<'\t' << "Test析构函数"<<endl;
}
Test(const Test &b)
{
a = b.a;
cout << b.a <<'\t'<< "Test拷贝构造函数" << endl;
}
};

输出为

0 Base无参构造函数
0 Base拷贝构造函数
0 Base拷贝构造函数
10 Test 构造函数
10 Test析构函数

使用了初始化列表以后,就可以直接调用拷贝构造函数。如果是传入引用,拷贝构造函数的调用就没有了。

  1. 类里面的任何成员变量在定义时是不能初始化的。
  2. 一般的数据成员可以在构造函数中初始化。
  3. const数据成员必须在构造函数的初始化列表中初始化。
  4. static要在类的定义外面初始化。
  5. 数组成员是不能在初始化列表里初始化的。
  6. 不能给数组指定明显的初始化。

3和5决定了,类成员中不能定义常量数组。

初始化列表中成员列出的顺序和它们在类中声明的顺序相同.

对一个对象的所有成员来说,它们的析构函数被调用的顺序总是和它们在构造函数里被创建的顺序相反。那么,如果允许上面的情况(即,成员按它们在初始化列表上出现的顺序被初始化)发生,编译器就要为每一个对象跟踪其成员初始化的顺序,以保证它们的析构函数以正确的顺序被调用。这会带来昂贵的开销。所以,为了避免这一开销,同一种类型的所有对象在创建(构造)和摧毁(析构)过程中对成员的处理顺序都是相同的,而不管成员在初始化列表中的顺序如何