构造函数
目的
- 在构造函数中可以进行各种初始化操作
优点
- 无序考虑类是否被初始化
- 经过构造函数完全初始化后的对象可以为
const
类型,也能更方便的被标准容器或算法使用
缺点
定义
-
构造函数是类的一种特殊的非静态成员函数,其特殊之处有三点:
- 构造函数的函数名必须与类名相同
- 构造函数无返回值
- 当我们创建类对象时构造函数会被自动调用,而不需要我们主动调用
-
构造函数用于初始化该类类型的对象
-
在类的构造函数定义中,成员初始化器列表指定各个直接和虚基类和各个非静态数据成员的初始化器。 (请勿与
std::initializer_list
混淆)- 成员初始化器列表,其语法是冒号字符 : 后随一或多个 成员初始化器 的逗号分隔列表
- 成员初始化器列表
-
构造函数必须不是
协程
(C++20 起)
非静态成员函数:非静态成员函数是声明于类的成员说明中,不带 static 或 friend 说明符的函数。
语法
类名 ( 形参列表(可选) ) 异常说明(可选) attr(可选)
类名
必须指明当前类(或者类模板的当前实例化) ,或当在命名空间作用域或在友元声明中声明时,它必须是有限定的类名。
成员初始化器列表,其语法是冒号字符 : 后随一或多个 成员初始化器 的逗号分隔列表,每项均具有下列语法
- 用直接初始化,或者当表达式列表为空时用值初始化,初始化类或者标识符所指的基类或成员
类或标识符 ( 表达式列表(可选) )
- 用列表初始化(如果列表为空则为值初始化,而在初始化聚合体时为聚合初始化),初始化类或者标识符所指的基类或成员
类或标识符 花括号初始化器列表
- 用包展开初始化多个基类
- 类或标识符 - 任何指明非静态数据成员的标识符,或任何指明该类自身 (对于委托构造函数)或直接或虚基类的类型名。
- 表达式列表 - 可为空的,传递给基类或成员的参数的逗号分隔列表
- 花括号初始化器列表 - 花括号包围的初始化器和嵌套的花括号初始化器列表的列表
- 形参包 - 变参模板形参包的名字
形参包 ...
解释
- 构造函数没有名字且无法被直接调用。
- 构造函数在发生初始化时调用,而且它们按照初始化的规则进行选择
- 在开始执行组成构造函数体的复合语句之前,所有直接基类,虚基类,及非静态数据成员的初始化均已结束。成员初始化器列表是能指定这些对象的非默认初始化之处。
- 对于不能默认初始化的基类和非静态数据成员,例如引用和const限定的类型的成员,必须指定成员初始化器
- 对没有成员初始化器的匿名联合体或者变体成员不进行初始化
类或标识符
指明虚基类的初始化器,在并非所构造对象的最终派生类的构造期间被忽略- 出现于
表达式列表
或花括号初始化器列表
中的名字在构造函数的作用域中求值:
class X {
int a, b, i, j;
public:
const int& r;
X(int i)
: r(a) // 初始化 X::r 为指代 X::a
, b{
i} // 初始化 X::b 为形参 i 的值
, i(i) // 初始化 X::i 为形参 i 的值
, j(this->i) // 初始化 X::j 为 X::i 的值
{
}
};
- 成员初始化器所抛出的异常可被
函数try块
处理 - 成员函数(包括虚成员函数)可从成员初始化器调用,但如果在该点所有基类尚未被全部初始化,则行为未定义
- 对于虚调用(如果在该点时已初始化直接基类),适用与从构造函数与析构函数中进行虚函数调用相同的规则:虚成员函数表现如同 *this 的动态类型是正在构造的类的静态类型(动态派发不在继承层级下传),而对纯虚成员函数的虚调用(但非静态调用)是未定义行为。
- 若非静态数据成员具有默认成员初始化器且同时出现在成员初始化器列表中,则使用成员初始化器而非默认成员初始化器
struct S {
int n = 42; // 默认成员初始化器
S() : n(7) {
} // 将设置 n 为 7,而非 42
};
- 引用成员不能绑定到成员初始化器列表中的临时量
struct A {
A() : v(42) {
} // 错误
const int& v;
};
变量的初始化在构造时提供其初值
继承的构造函数
https://zh.cppreference.com/w/cpp/language/using_declaration
例子
struct S {
int n;
S(int); // 构造函数声明
S() : n(7) {
} // 构造函数定义。
// ": n(7)" 为初始化器列表
// ": n(7) {}" 为函数体
};
S::S(int x) : n{
x} {
} // 构造函数定义。": n{x}" 为初始化器列表
int main()
{
S s; // 调用 S::S()
S s2(10); // 调用 S::S(int)
}