C++ 初始化列表

28 Aug 2024 | 5 分钟阅读

引言

类的 **数据成员** 使用 **初始化列表** 进行初始化。构造函数将成员的 **初始化列表** 指定为冒号后跟着逗号分隔的列表。以下是使用 **初始化列表** 初始化 Point 类的 x 和 y 属性的示例。

输出

x = 11, y = 17

说明

上面显示的代码仅作为 **初始化列表语法** 的说明。在上面的代码中,在构造函数中 **初始化 x** 和 **y** 也是一个简单的过程。但是,在某些情况下,必须使用 **初始化列表** 而不是在构造函数内部初始化数据成员。这些情况如下:

1) 用于非静态 const 数据成员的初始化

**Const 数据成员** 需要使用 **初始化列表** 进行初始化。在下面的示例中,"t" 是 Test 类的 **初始化列表** 初始化的 const 数据成员。**Const 数据成员** 在 **初始化列表** 中初始化,因为没有专门为它们分配内存;相反,它们被折叠到 **符号表** 中,因此需要在 **初始化列表** 中进行初始化。

此外,由于它是带参数的构造函数,我们无需调用赋值运算符,这使我们免于执行额外的操作。

输出

10

2) 用于引用成员的初始化

**初始化列表** 必须用于初始化引用成员。在下面的示例中,"t" 是 Test 类的一个 **初始化列表** 初始化的引用成员。

输出

	50
	60

3) 用于没有默认构造函数的成员对象的初始化

如下面的示例所示,类 **"A"** 没有 **默认构造函数**,而 **类 "A"** 的对象 **"a"** 是类 **"B"** 的 **数据成员**。需要使用初始化列表来初始化 **"a"**。

输出

Constructor A is called: Value of i: 50
Constructor B is called

说明

如果我们想使用默认构造函数初始化 **"a"**,则不需要 **初始化列表**,但是如果 **类 A** 既有 **默认构造函数** 又有 **带参数的构造函数**,并且我们想使用 **带参数的构造函数** 初始化 **"a"**,则需要 **初始化列表**。

4) 用于基类成员的初始化

类似于第 3 点,只有 **初始化列表** 才能用于调用基类的 **带参数的构造函数**。

输出

Constructor A is called: Value of i: 46
Constructor B is called

5) 当数据成员的名称与构造函数参数相同

如果 **构造函数参数** 的名称与数据成员的名称匹配,则必须使用 this **指针** 或 **初始化列表** 来初始化数据成员。在下面的示例中,**A()** 的 **成员名** 和 **参数名** 都是 **"i"**。

输出

45

6) 为了性能

最好在 **初始化列表** 中初始化所有类变量,而不是在函数体内部赋值。考虑以下示例:

编译器在此处使用以下步骤生成 **MyClass** 类型的对象。

首先为 **"a"** 调用该类型的构造函数。

**MyClass() 构造函数** 调用 **"Type"** 的赋值运算符进行赋值。

编译器处理 **初始化列表** 的过程如下:

  1. 首先,**"a"** 被传递给该类型的构造函数。
  2. **"Type"** 类的带参数构造函数用于初始化 **变量(a)**。使用 **初始化列表** 中的参数直接复制构造 **"变量"**。
  3. 由于 **"a"** 超出 **"Type"** 的作用域,因此调用 **"Type"** 的析构函数。
    这个例子演示了在构造函数体内部使用赋值会导致三次函数调用:**构造函数、析构函数** 和一次 **加法赋值运算符** 调用。此外,当使用初始化列表时,只有两次函数调用:一次 **复制构造函数** 调用和一次 **析构函数** 调用。
  4. 最后,由于 **"a"** 不再位于 **"Type"** 的作用域内,因此调用 **"Type"** 的析构函数。

考虑修改后的相同代码,在 **MyClass() 构造函数** 中包含 **初始化列表**。