构造函数和成员初始化列表
C++
编译器支持
自由(freestanding)与宿主(hosted)
语言
标准库
标准库头文件
具名要求
特性测试宏 (C++20)
语言支持库
概念库 (C++20)
诊断库
内存管理库
元编程库 (C++11)
通用工具库
容器库
迭代器库
范围库 (C++20)
算法库
字符串库
文本处理库
数值库
日期和时间库
输入/输出库
文件系统库 (C++17)
并发支持库 (C++11)
执行控制库 (C++26)
技术规范
符号索引
外部库
[编辑] C++ 语言
通用主题
预处理器
注释
关键词
转义序列
流程控制
条件执行语句
if
switch
迭代语句(循环)
for
range-for (C++11)
while
do-while
跳转语句
continue - break
goto - return
函数
函数声明
Lambda 函数表达式
inline 说明符
动态异常规范 (直到 C++17*)
noexcept 说明符 (C++11)
异常
throw 表达式
try 块
catch 处理程序
命名空间
命名空间声明
命名空间别名
类型
基本类型
枚举类型
函数类型
类/结构体类型
联合类型
说明符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
存储期说明符
初始化
默认初始化
值初始化
零初始化
复制初始化
直接初始化
聚合初始化
列表初始化 (C++11)
常量初始化
引用初始化
表达式
值类别
求值顺序
运算符
运算符优先级
替代表示
字面量
布尔 - 整型 - 浮点型
字符 - 字符串 - nullptr (C++11)
用户定义 (C++11)
工具
属性 (C++11)
类型
typedef 声明
类型别名声明 (C++11)
类型转换
隐式转换
static_cast
const_cast
显式转换
dynamic_cast
reinterpret_cast
内存分配
new 表达式
delete 表达式
类
类声明
构造函数
this 指针
访问说明符
friend 说明符
类特有的函数属性
虚函数
override 说明符 (C++11)
final 说明符 (C++11)
explicit (C++11)
static
特殊成员函数
默认构造函数
复制构造函数
移动构造函数 (C++11)
复制赋值
移动赋值 (C++11)
析构函数
模板
类模板
函数模板
模板特化
参数包 (C++11)
杂项
内联汇编
C++ 历史
[编辑] 类
通用
概览
class/struct 类型
union 类型
注入类名
类属性说明符 (C++26)
成员
数据成员
静态成员
this 指针
嵌套类
成员模板
位域
using-声明
成员函数
成员访问说明符
构造函数和成员初始化列表
默认成员初始化器 (C++11)
friend 说明符
explicit 说明符
转换构造函数
特殊成员函数
默认构造函数
复制构造函数
移动构造函数 (C++11)
复制赋值运算符
移动赋值运算符 (C++11)
析构函数
继承
基类和派生类
空基类优化 (EBO)
虚成员函数
纯虚函数和抽象类
override 说明符 (C++11)
final 说明符 (C++11)
[编辑]
构造函数是非静态的成员函数,它们使用特殊的声明符语法进行声明,用于初始化其类类型的对象。
构造函数不能是协程。
(C++20 起)
构造函数不能有显式对象参数。
(C++23 起)
目录
1 语法
2 成员初始化列表
3 解释
3.1 构造和析构期间的操作
3.2 委托构造函数
3.3 继承构造函数
3.4 初始化顺序
4 注意
5 示例
6 缺陷报告
7 参考文献
8 参阅
[编辑] 语法
构造函数使用以下形式的成员函数声明符进行声明
类名 ( 参数列表 (可选) ) 异常规范 (可选) 属性 (可选)
类名
-
一个标识符表达式,可能后跟属性列表,并(C++11 起)可能用一对括号括起来
参数列表
-
参数列表
异常规范
-
动态异常规范
(C++11 前)
或者动态异常规范或者noexcept 规范
(C++11 起)(C++17 前)
noexcept 规范的一部分
(C++17 起)
属性
-
(C++11 起) 属性列表
构造函数声明的声明说明符中唯一允许的说明符是friend、inline、constexpr(C++11 起)、consteval(C++20 起)和explicit(特别地,不允许返回类型)。注意,cv-和引用限定符也不允许:正在构造的对象的 const 和 volatile 语义只在最派生构造函数完成之后才生效。
类名的标识符表达式必须具有以下形式之一
在友元声明中,标识符表达式是限定标识符,它命名一个构造函数。否则,在属于类或类模板的成员规范的成员声明中
对于类,标识符表达式是立即封闭类的注入类名。对于类模板,标识符表达式是命名当前实例化的类名(C++20 前)注入类名(C++20 起)的立即封闭类模板。
否则,标识符表达式是限定标识符,其末尾的非限定标识符是其查找上下文的注入类名。
[编辑] 成员初始化列表
任何构造函数的函数定义的主体,在复合语句的开括号之前,可以包含成员初始化列表,其语法是冒号字符:,后跟一个或多个逗号分隔的成员初始化器列表,每个成员初始化器具有以下语法
类或标识符 ( 表达式列表 (可选) )
(1)
类或标识符 大括号初始化列表
(2)
(C++11 起)
参数包 ...
(3)
(C++11 起)
1) 使用直接初始化初始化由类或标识符命名的基类或成员,如果表达式列表为空,则使用值初始化
2) 使用列表初始化初始化由类或标识符命名的基类或成员(如果列表为空,则变为值初始化,如果初始化聚合类型,则变为聚合初始化)
3) 使用包扩展初始化多个基类
类或标识符
-
任何命名非静态数据成员的标识符,或任何命名类本身(用于委托构造函数)或直接基类或虚基类的类型名。
表达式-列表
-
可能为空的逗号分隔列表,表示要传递给基类或成员构造函数的参数
大括号初始化列表
-
大括号括起来的初始化列表
参数包
-
可变模板参数包的名称
运行此代码
struct S
{
int n;
S(int); // constructor declaration
S() : n(7) {} // constructor definition:
// ": n(7)" is the initializer list
// ": n(7) {}" is the function body
};
S::S(int x) : n{x} {} // constructor definition: ": n{x}" is the initializer list
int main()
{
S s; // calls S::S()
S s2(10); // calls S::S(int)
}
[编辑] 解释
构造函数没有名称,不能直接调用。它们在初始化发生时被调用,并根据初始化规则进行选择。没有explicit说明符的构造函数是转换构造函数。带有constexpr说明符的构造函数使其类型成为字面类型。可以不带任何参数调用的构造函数是默认构造函数。接受相同类型的另一个对象作为参数的构造函数是拷贝构造函数和移动构造函数。
在构成构造函数函数体的复合语句开始执行之前,所有直接基类、虚基类和非静态数据成员的初始化都已完成。成员初始化列表是指定这些子对象非默认初始化的地方。对于不能默认初始化的基类和不能通过默认初始化或通过它们的默认成员初始化器(如果有)初始化的非静态数据成员(C++11 起),例如引用类型和 const 限定类型的成员,必须指定成员初始化器。(请注意,如果成员类型或初始化器是依赖的,则类模板实例化中非静态数据成员的默认成员初始化器可能无效。)(C++11 起)对于没有成员初始化器或默认成员初始化器(C++11 起)的匿名联合体或变体成员,不执行任何初始化。
在构造不是正在构造的对象的最终派生类的任何类期间,类或标识符命名虚基类的初始化器将被忽略。
表达式列表或大括号初始化列表中出现的名称在构造函数的作用域中进行求值
class X
{
int a, b, i, j;
public:
const int& r;
X(int i)
: r(a) // initializes X::r to refer to X::a
, b{i} // initializes X::b to the value of the parameter i
, i(i) // initializes X::i to the value of the parameter i
, j(this->i) // initializes X::j to the value of X::i
{}
};
从成员初始化器抛出的异常可以通过函数try块来处理。
如果非静态数据成员具有默认成员初始化器,并且也出现在成员初始化列表中,则使用成员初始化器并忽略默认成员初始化器
struct S
{
int n = 42; // default member initializer
S() : n(7) {} // will set n to 7, not 42
};
(C++11 起)
引用成员不能在成员初始化列表中绑定到临时对象
struct A
{
A() : v(42) {} // Error
const int& v;
};
注意:同样适用于默认成员初始化器。
[编辑] 构造和析构期间的操作
成员函数(包括虚成员函数)可以对正在构造或析构的对象进行调用。同样,正在构造或析构的对象可以是typeid或dynamic_cast的操作数。
但是,如果在以下任何评估期间执行这些操作,则行为是未定义的
构造函数的前置条件断言的评估析构函数的后置条件断言的评估
(C++26 起)
在所有基类的成员初始化器完成之前,成员初始化列表的评估
委托构造函数
如果类名本身作为类或标识符出现在成员初始化列表中,则该列表必须仅包含这一个成员初始化器;这样的构造函数被称为委托构造函数,初始化列表中唯一的成员选择的构造函数是目标构造函数。
在这种情况下,目标构造函数通过重载决议被选中并首先执行,然后控制返回到委托构造函数,并执行其函数体。
委托构造函数不能递归。
class Foo
{
public:
Foo(char x, int y) {}
Foo(int y) : Foo('a', y) {} // Foo(int) delegates to Foo(char, int)
};
继承构造函数
参见using 声明。
(C++11 起)
[编辑] 初始化顺序
列表中成员初始化器的顺序无关紧要:实际的初始化顺序如下
1) 如果构造函数是用于最派生类,则虚基类按照它们在基类声明的深度优先、从左到右遍历中出现的顺序进行初始化(从左到右指的是在基类指定符列表中的出现顺序)。
2) 然后,直接基类按照它们在该类的基类指定符列表中从左到右的顺序进行初始化。
3) 然后,非静态数据成员按照它们在类定义中的声明顺序进行初始化。
4) 最后,执行构造函数的主体。
(注意:如果初始化顺序由不同构造函数的成员初始化列表中出现的顺序控制,则析构函数将无法确保析构顺序与构造顺序相反。)
[编辑] 注意
功能测试宏
值
标准
特性
__cpp_delegating_constructors
200604L
(C++11)
委托构造函数
[编辑] 示例
运行此代码
#include
#include
#include
struct Base
{
int n;
};
struct Class : public Base
{
unsigned char x;
unsigned char y;
std::mutex m;
std::lock_guard
std::fstream f;
std::string s;
Class(int x) : Base{123}, // initialize base class
x(x), // x (member) is initialized with x (parameter)
y{0}, // y initialized to 0
f{"test.cc", std::ios::app}, // this takes place after m and lg are initialized
s(__func__), // __func__ is available because init-list is a part of constructor
lg(m), // lg uses m, which is already initialized
m{} // m is initialized before lg even though it appears last here
{} // empty compound statement
Class(double a) : y(a + 1),
x(y), // x will be initialized before y, its value here is indeterminate
lg(m)
{} // base class initializer does not appear in the list, it is
// default-initialized (not the same as if Base() were used, which is value-init)
Class()
try // function try block begins before the function body, which includes init list
: Class(0.0) // delegate constructor
{
// ...
}
catch (...)
{
// exception occurred on initialization
}
};
int main()
{
Class c;
Class c1(1);
Class c2(0.1);
}
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告
应用于
发布时的行为
正确的行为
CWG 194
C++98
构造函数的声明符语法只允许最多一个函数说明符(例如,构造函数不能声明为inline explicit)
允许使用多个函数说明符
CWG 257
C++98
未指定抽象类是否应为其虚基类提供成员初始化器
指定为不需要并且此类成员初始化器在执行期间被忽略
CWG 263
C++98
构造函数的声明符语法禁止构造函数成为友元
允许构造函数成为友元
CWG 1345
C++98
没有默认成员初始化器的匿名联合体成员被默认初始化
它们未被初始化
CWG 1435
C++98
构造函数声明符语法中“类名”的含义不明确
将语法更改为专门的函数声明符语法
CWG 1696
C++98
引用成员可以初始化为临时对象(其生命周期将在构造函数结束时结束)
此类初始化是格式错误的
[编辑] 参考文献
C++23 标准 (ISO/IEC 14882:2024)
11.4.5 构造函数 [class.ctor]
11.9.3 初始化基类和成员 [class.base.init]
C++20 标准 (ISO/IEC 14882:2020)
11.4.4 构造函数 [class.ctor]
11.10.2 初始化基类和成员 [class.base.init]
C++17 标准 (ISO/IEC 14882:2017)
15.1 构造函数 [class.ctor]
15.6.2 初始化基类和成员 [class.base.init]
C++14 标准 (ISO/IEC 14882:2014)
12.1 构造函数 [class.ctor]
12.6.2 初始化基类和成员 [class.base.init]
C++11 标准 (ISO/IEC 14882:2011)
12.1 构造函数 [class.ctor]
12.6.2 初始化基类和成员 [class.base.init]
C++98 标准 (ISO/IEC 14882:1998)
12.1 构造函数 [class.ctor]
12.6.2 初始化基类和成员 [class.base.init]
[编辑] 参见
复制省略
转换构造函数
复制赋值
复制构造函数
默认构造函数
析构函数
explicit
初始化
聚合初始化
常量初始化
复制初始化
默认初始化
直接初始化
列表初始化
引用初始化
值初始化
零初始化
移动赋值
移动构造函数
new