C++ class constructor and destructor

构造函数

每个类都会定义它的对象被初始化的方式,类通过一个或者几个特殊的成员函数控制每个类的初始化过程,这些函数叫做构造函数。
构造函数的几个特征:

  1. 构造函数的任务是初始化类对象的数据成员,无论何时只要类的对象被创建,就会执行构造函数。
  2. 构造函数名字和类名相同。
  3. 构造函数没有返回类型。
  4. 构造函数可以有多个,和重载函数差不多。
  5. 构造函数不能声明为const。当我们要创建一个const对象时,一直到构造函数完成初始化过程,对象才能获得常量属性。因此,构造函数在const对象的构造过程中可以向其写入。或者说,构造函数要更改对象,如果声明为const的话,就无法更改了。

默认构造函数

当类没有声明任何构造函数时,编译器提供一个特殊的构造函数,称为默认构造函数,默认构造函数不需要任何实参。默认构造函数的初始化规则为:如果存在类内初始值,用它初始化成员,否则使用默认初始化。
对于非常简单的类,可以使用默认构造函数,但是对于特别复杂的类,必须定义它自己的默认构造函数,有三个原因:

  1. 编译器只有在发现类中不包含任何构造函数的情况下才会生成一个默认的构造函数.如果我们定义了一些其他的构造函数,那么编译器就不会提供默认构造函数了,如果需要默认构造函数需要程序员自己定义.
  2. 编译器提供的默认构造函数可能指定错误的操作,因为执行默认初始化时,块中的内置类型或者复合类型的对象都是随机值,所以需要自己自己定义一个默认构造函数.
  3. 编译器不能为某些类生成一个默认构造函数,如果类中包含一个其他类类型的成员,并且这个成员没有默认构造函数,那么编译器无法初始化该成员.这样的类,必须由程序员自己定义默认构造函数,否则没有可用的默认构造函数.

默认构造函数的作用

编译器生成的默认构造函数(是编译器需要)

Nontrivial default construct是编译器需要的那种构造函数,必要的话由编译器合成,它不会负责对data member进行初始化,这是程序员的责任。总共有四种nontrivial default construct:

  1. 带有default constructor的member class object;
  2. 带有default constructor的base class;
  3. 带有virtual function的class;
  4. 带有virtual base class的class。

拷贝构造函数的构建操作

有三种情况,会把一个object的内容当做另一个object的初值,即调用拷贝构造函数:

  1. 用=号初始化一个对象。
  2. 值传参。
  3. 函数返回值。

默认memberwise的初始化

如果类没有提供explicit copy constructor,当使用类对象初始化另一个类对象时,其实是使用默认memberwise初始化完成的。
copy constructor只有在必要的时候通过编译器产生出来。

Bitwise Copy Semantics

定义构造函数

构造函数是可以重载的,一个类可以有多个构造函数。

=default的含义

=default是C++ 11标准的新用法,如果需要编译器提供的默认构造函数,在参数列表最后加上=default即可.其中,=default可以和声明一起出现在类的内部,也可以作为定义出现在类的外部.如果=default在类的内部,那么默认构造函数是内联函数,如果它在类的外部,该成员默认情况下不是内联的.

初始值列表

什么是初始值列表

在参数列表之后和花括号之前,加上冒号和一个特殊的列表,这个特殊的列表叫做构造函数初始值列表.这个列表由多个 成员变量的名字,括号括起来的成员变量初始值构成,不同成员变量的初始化通过逗号分隔开来.
如果初始值列表没有对部分或者全部数据成员变量进行初始化,那么这些的数据成员变量将根据类内初始值进行初始化,如果没有类内初始化值,执行默认初始化.

为什么要使用初始值列表???

初始值列表进行的是初始化,而在构造函数体内进行初始化执行的是赋值。对于引用和const对象来说,只能使用初始化,而不能使用赋值。

成员初始化的顺序

初始值列表只说明用于初始化的值,而不能限制初始化的具体顺序。成员初始化的顺序和他们在类定义中出现的顺序一致。

拷贝构造函数,拷贝赋值函数以及析构函数

这三个函数很重要,通常在含有指针成员的类中,需要定义拷贝构造函数和拷贝赋值函数。
在使用等号进行初始化,即拷贝初始化的时候,使用的是拷贝构造函数,先创建一个对象,然后调用拷贝构造函数复制这个对象到新创建的对象。
而不使用等号的初始化,也就是直接初始化时,使用的是函数匹配,即选择最合适的构造函数进行初始化。

委托构造函数

一个委托构造函数使用他所属的类中的其他构造函数执行它自己的初始化过程,或者说它把子集的一些或者全部工作委托给了其他构造函数。

explict构造函数

转换构造函数和重载函数调用运算符能够把一个类类型转换为另外一种类类型。
在需要使用某个类的对象的时候,有时候可以仅仅传递实参进入,如果该类有相应的构造函数可以创建该类对象的时候,编译器会自动的构造一个类对象参与运算。
可以通过在构造函数前面加上explict,阻止编译器隐式的调用构造函数进行转换,只有显式的调用构造函数时才可以。

explicit构造函数的性质:

  1. explicit只能出现在类内部。
  2. explicit只能用于直接初始化,不能用于拷贝初始化。
  3. static_cast可以使用explicit构造函数。
  4. 标准库中含有显示构造函数的类,接收一个容量参数的vector构造函数是explicit的。

聚合类

满足下列要求的类称为聚合类:

  1. 所有成员都是public的;
  2. 没有定义任何构造函数,可以定义其他函数(需要满足下面的条件);
  3. 没有指定类内初始值;
  4. 没有基类,也没有virtual function。

使用初始值列表进行初始化。它的缺点:

  1. 所有成员都是public
  2. 让类的用户进行初始化,而不是让类的作者。
  3. 添加或者删除一个成员后,所有的初始化语句都要改变。

字面值常量类

参考文献

1.《C++ Primer》第五版中文版