因为C++标准中并未规定class中的member variables有默认的初始值,所以在构建class的object之前,最好对class中所有的member variables进行初始化。
class构造函数的initialization list是真初始化,而构造函数中的赋初值操作是伪初始化。这两种初始化的本质区别就是赋值的过程不同:真初始化是通过复制构造函数进行赋值操作的,而伪初始化则是首先利用默认构造函数构造一个对象,然后调用=操作符进行赋值的。正是由于这个区别,const和reference这两种不能被赋值的类型就不能被伪初始化,只能在initialization list中进行真初始化。
可以看出,在member variables不多且内建类型较少的情况下,应该采用真初始化,因为这样可以减少初始化时的函数调用,提高效率。而因为内建类型的创建并不需要构造函数调用的消耗,所以不在考虑之列。而当member variables很多或是有多个构造函数的时候,应该创建一个private的初始化函数,在该函数中对所有的member variables进行伪初始化(赋初值),然后在构造函数中调用它。这样可以避免冗余的代码。当然,这样会带来额外的函数调用的消耗。
需要注意的一点是,无论initialization list中变量出现的顺序如何,class中member variables的初始化顺序都是按照相同控制域(控制域有public,protected,private)中声明出现的顺序进行的,而非initialization list中变量出现的顺序!因为若是初始化的顺序按照列表中的顺序,则必须为每个类的不同初始化顺序的对象保持一份记录,以确保析构的时候能够按照逆序进行,这种做法会大大降低执行效率,尤其是在大型程序中。要记住的是不同控制域间的变量初始化顺序在C++标准中是没有定义的!不过,在cl编译器中,member variables的初始化顺序就是按照声明出现的顺序进行的,而与不同的控制域无关。当然,gcc中未必如此。
在C++中,编译器会在需要的时候在class中添加如下内容:
默认构造函数
复制构造函数
=操作符
非虚的析构函数
&操作符
当我们不希望以上的某些函数会被使用时,可以通过将其置于该类的private的控制域中,并且只声明不定义来实现禁止使用。一旦调用,则会在链接的过程中报错;亦可以在private的父类中这么做,从而实现在编译过程中报错的目的。这是一种通用的方法。其中默认构造函数可以通过定义带参数的构造函数来禁用。
在这里需要重点讨论一下析构函数。当某个类是多态链中的某个类的父类时,它必须要有virtual的析构函数,因为一旦使用该类的指针指向一个构建于heap上的子类时,那么delete该指针时会因为静态链接的原因而直接调用该类的析构函数而不是子类的析构函数,这样的行为在C++标准中是没有被定义的。如果该类不处于多态链中,则不可为virtual。