构造函数再探
构造函数初始值列表
初始化与赋值
构造函数使用初始值列表还是在函数体中给数据赋值的区别在于,前者是初始化了它的数据成员,后者是对数据成员执行了赋值操作。
在很多类中,初始化和赋值的区别事关底层效率问题:前者直接初始化数据成员,后者则先初始化再赋值。
除了效率外更重要的是,一些数据成员必须被初始化。如果成员是const、引用,或者属于某种未提供默认构造函数的类类型,我们必须通过构造函数初始值列表为这些成员提供初始值。
成员初始化的顺序
一般来说,初始化的顺序没什么特别要求。
最好令构造函数初始值列表的顺序与成员声明的顺序保持一致。而且如果可能的话,尽量避免使用某些成员初始化其他成员。
默认实参与构造函数
如果一个构造函数为所有参数都提供了默认实参,则它实际上也定义了默认构造函数。
委托构造函数
一个委托构造函数使用它所属类的其他构造函数执行它自己的初始化过程,或者说它把它自己的一些(或者全部)职责委托给了其他构造函数。
和其他构造函数一样,一个委托构造函数也有一个成员初始值列表和一个函数体。在委托构造函数内,成员初始值列表只有一个唯一的入口,就是类名本身。
默认构造函数的作用
当对象被默认初始化或值初始化时自动执行默认构造函数。
默认初始化:
当我们在块作用域中不适用任何初始值定义一个非静态变量或数组时;
当一个类本身含有类类型的成员且使用合成的默认构造函数时;
当类类型的成员没有构造函数初始值列表中显示初始化时。
在实际中,如果定义了一个其他构造函数,那么最好也提供一个默认构造函数。
值初始化:
在数组初始化的过程中如果我们提供的初始值数量少于数组的大小时;
当我们不使用初始值定义一个局部静态变量时;
当我们通过书写形如T( )的表达式显示地请求值初始化时,其中T是类型名。
类必须包含一个默认构造函数以便在上述情况下使用,其中的大多数情况非常容易判断。
在实际中,如果定义了其他构造函数,那么最好也提供一个默认构造函数。
隐式的类类型转换
我们能为类定义隐式转换规则。如果构造函数只接受一个实参,则它实际上定义了转换为此类类型的隐式转换机制,有时我们把这种构造函数称作转换构造函数。
只允许一步类类型转换
编译器只会自动地执行一步类型转换。
类类型转换不总是有效
有这样一种情况:构造函数只接受一个实参,实际上,我们构建了一个对象,先将它的值加到调用他的对象中,随后将其丢弃。
抑制构造函数定义的隐式转换
在要求隐式转换的程序上下文中,我们可以通过将构造函数声明为explicit加以阻止。
只能在类内声明构造函数时使用explicit关键字,在类外部定义时不应重复。
explicit构造函数只能用于直接初始化
发生隐式转换的一种情况是当我们执行拷贝形式的初始化时(使用=)。此时,我们只能使用直接初始化而不能使用explicit构造函数。
为转换显示的使用构造函数
尽管编译器不会将explicit的构造函数用于隐式转换过程,但是我们可以使用这样的构造函数显示地强制进行转换:
item.combine(Sales_data(null_book));
标准库中含有显式构造函数的类
接受一个单参数的const char*的string构造函数不是explicit的。
接受一个容量参数的vector构造函数是explicit的。
聚合类
聚合类使得用户可以直接访问其成员,并且具有特殊的初始化语法形式。
当一个类满足如下条件时,我们说它是聚合的:
●所有成员都是public的。
●没有定义任何构造函数。
●没有类内初始值。
●没有基类,也没有virtual函数。
我们可以提供一个花括号括起来的成员初始值列表,并用它初始化聚合类的数据成员。初始值的顺序必须与声明的顺序一致。
与初始化数组元素的规则一样,如果初始值列表中的元素个数少于类的成员数量,则靠后的成员被值初始化。初始值列表的元素个数绝对不能超过类的成员数量。
值得注意的是,显式地初始化类的对象的成员存在三个明显的缺点:
●要求类的所有成员都是public的。将正确初始化每个对象的每个成员的重任交给了类的用户(而非类的作者)。因为用户很容易忘掉某个初始值,或者提供一个不恰当的初始值,所以这样的初始化过程冗长乏味且容易出错。
●添加或删除一个成员之后,所有的初始化语句都需要更新。
字面值常量类
constexpr函数的参数和返回值必须是字面值类型。除了算术类型、引用和指针外,某些类也是字面值类型。
数据成员都是字面值类型的聚合类是字面值常量类。
如果一个类不是聚合类,但它符合下述要求,则它也是一个字面值常量类:
●数据成员都必须是字面值类型。
●类必须至少含有一个constexpr构造函数。
●如果一个数据成员含有类内初始值,则内置类型成员的初始值必须是一条常量表达式;或者如果成员属于某种类类型,则初始值必须使用成员自己的constexpr构造函数。
●类必须使用析构函数的默认定义,该成员负责销毁类的对象。
constexpr构造函数
尽管构造函数不能是const的,但是字面值常量类的构造函数可以是constexpr函数。事实上,一个字面值常量类必须至少提供一个constexpr构造函数。
constexpr构造函数可以声明为=default的形式或是删除函数的形式。否则,他能拥有的唯一可执行的语句就是返回语句。一般来说constexpr构造函数体应该是空的。
constexpr构造函数必须初始化所有数据成员,初始值或者使用constexpr构造函数,或者是一条常量表达式。
constexpr构造函数用于生成constexpr对象以及constexpr函数的参数或返回类型。