构造函数与初始化列表
初始化列表只能用于构造函数
在类的实现中,构造函数体内“初始化”的实际上是赋值而不是初始化。也就是说,当代码运行到构造函数内部时,初始化列表已经执行完了,因此相当于是先初始化了一遍,然后又赋值了一遍,重复计算,浪费效率,因此应该优先使用初始化列表。同时,当没有默认的无参构造函数时,就一定会使用初始化列表。(即使自己没有在含参构造函数中都是基本数据类型, 不强制显式使用初始化列表, 也会自动调用初始化列表, 而在构造函数内部执行的仅仅是赋值)
创建派生类对象时,程序首先调用基类构造函数,然后再调用派生类构造函数。基类构造函数负责初始化继承的数据成员;派生类构造函数主要用于初始化新增的数据成员。派生类构造函数总是需要调用一个基类构造函数。当基类没有默认的构造函数时,就必须显式指明调用哪一个构造函数。
在继承关系中, 必须显式的在初始化列表中对基类初始化, 因为只有基类初始化完成后, 才能进行子类初始化, 而当进入子类构造函数内部时, 子类初始化已经完成.
注意:除了虚基类以外,类只能将值传递会相邻的基类,但后者可以使用相同的机制将信息传递给上层相邻的基类,以此类推。
C++的类对象创建过程
C++ 在创建类时需要经过两个阶段:分配空间(Allocation)和初始化(Initialization)
分配空间
创建C++类对象的第一步就是为其分配内存空间。对于全局对象,静态对象以及分配在栈区域内的对象,对它们的内存分配是在编译阶段就完成了,而对于分配在堆区域内的对象,它们的分配是在运行时动态进行的。内存空间的分配过程涉及到两个关键的问题:需要分配空间的大小以及是否有足够的内存空间来满足分配。
初始化
首先需要区分两个概念:初始化(Initialization)和赋值(Assignment)。初始化早于赋值,它是随着对象的诞生一起进行的。而赋值是在对象诞生以后又给予它一个新的值。
在C++中,提供了类成员的初始化列表,并且初始化列表是先于构造函数体内的代码执行的。
哪些情况下只能用初始化列表(initialization list) 而不能用赋值 (assignment)
对于以下三种情况,必须使用成员初始化列表
- 需要初始化的数据成员是对象的情况(包含继承情况下,通过显示调用父类的构造函数对父类数据成员进行初始化)
- 需要初始化const修饰的类成员
- 需要初始化引用成员数据
- 子类初始化父类的私有成员,需要在(并且只能在)参数初始化列表中显示调用父类的构造函数。(这种其实可以并到第一种情况,因为初始化私有成员,就意味着初始化对象)
类对象是默认使用初始化列表的,当没有无参构造函数时,就必须显式使用初始化列表(所以不论如何,都会使用到初始化列表)。
当类成员中含有一个const对象时,或者一个引用时,必须经过成员初始化列表进行初始化,因为const对象或者引用在声明的同时必须初始化,而在构造函数中,做的是对它们的赋值,并不是初始化。
初始化列表的顺序
构造函数需要初始化的数据成员,不论是否显式的出现在构造函数的成员初始化列表中,都会在该处完成初始化,并且初始化的顺序和变量声明时的顺序是一致的,与列表中的先后顺序无关