命名空间
变体
操作

构造函数与成员初始化列表

来自 cppreference.cn
< cpp‎ | language
 
 
C++ 语言
通用主题
流程控制
条件执行语句
if
迭代语句(循环)
for
范围 for (C++11 起)
跳转语句
函数
函数声明
Lambda 函数表达式
inline 说明符
动态异常规范 (在 C++17* 中弃用)
noexcept 说明符 (C++11 起)
异常
命名空间
类型
说明符
const/volatile
decltype (C++11 起)
auto (C++11 起)
constexpr (C++11 起)
consteval (C++20 起)
constinit (C++20 起)
存储期说明符
初始化
表达式
备选表示
字面量
布尔 - 整数 - 浮点
字符 - 字符串 - nullptr (C++11 起)
用户定义 (C++11 起)
实用工具
属性 (C++11 起)
类型
typedef 声明
类型别名声明 (C++11 起)
转型
内存分配
类声明
构造函数
this 指针
类特定的函数属性
虚函数
override 说明符 (C++11 起)  
final 说明符 (C++11 起)
explicit (C++11 起)
static

特殊成员函数
模板
模板特化
形参包 (C++11 起)
其他
 
 

构造函数 是使用特殊的声明符语法声明的非静态 成员函数,它们用于初始化其类类型的对象。

构造函数不能是协程

(C++20 起)

构造函数不能具有显式对象形参

(C++23 起)

内容

[编辑] 语法

构造函数使用下列形式的成员函数声明符声明

类名 ( 形参列表(可选) ) except(可选) attr(可选)
类名 - 一个标识符表达式可能后随属性列表,并且(C++11 起)可能被一对圆括号括起来
形参列表 - 形参列表
except -

动态异常规范

(C++11 前)

或者动态异常规范
或者noexcept 规范

(C++11 起)
(C++17 前)

noexcept 规范

(C++17 起)
attr - (C++11 起) 属性列表

在构造函数声明的声明说明符中,唯一允许的说明符是 friendinlineconstexpr(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;
};

注意:这同样适用于默认成员初始化器

[编辑] 构造与析构期间的操作

成员函数(包含虚成员函数)可以为正在构造或析构的对象调用。类似地,正在构造或析构的对象可以是 typeiddynamic_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) 最后,执行构造函数的函数体。

(注意:如果初始化顺序由不同构造函数的成员初始化列表中的出现顺序控制,那么析构函数将无法确保析构顺序与构造顺序相反。)

[编辑] 注解

特性测试宏 Std 特性
__cpp_delegating_constructors 200604L (C++11) 委托构造函数

[编辑] 示例

#include <fstream>
#include <string>
#include <mutex>
 
struct Base
{
    int n;
};   
 
struct Class : public Base
{
    unsigned char x;
    unsigned char y;
    std::mutex m;
    std::lock_guard<std::mutex> lg;
    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++ 标准。

DR 应用于 已发布行为 正确行为
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]

[编辑] 参见