C++11 使用 {} 进行初始化
文章目录
- C++11 使用 {} 进行初始化
- 1 列表初始化的基本概念
- 2 列表初始化的用法
- 2.1 基本数据类型
- 2.2 数组
- 2.3 类和结构体
- 2.4 容器初始化
- 3 列表初始化的优势
- 4 注意事项
- 5 示例代码
- 6 总结
C++11 使用 {} 进行初始化
1 列表初始化的基本概念
在 C++11 及之后的版本中,使用大括号 {}
进行初始化得到了显著增强,并且被赋予了新的名称:列表初始化(list initialization)或统一初始化(uniform initialization)。这种初始化方式提供了更高的类型安全性和更清晰的语法,特别是在避免某些类型的构造函数歧义方面。
2 列表初始化的用法
以下是一些在 C++11 中使用 {}
进行初始化的关键点和示例:
2.1 基本数据类型
对于基本数据类型(如 int
, double
, char
等),你可以使用 {}
进行初始化,但通常这不是必需的,因为直接使用 =
也可以达到相同的效果。然而,{}
可以在某些情况下避免潜在的窄化转换警告或错误。
int a{10}; // 整数初始化
double b{3.14}; // 浮点数初始化
char c{'A'}; // 字符初始化
bool d{true}; // 布尔值初始化
当使用空的 {}
语法进行初始化时,编译器会自动将剩余的部分初始化为 0 或空值,这有助于避免未初始化变量的问题。
struct Sample {int a{}; // 初始化为 0double b{}; // 初始化化为 0.0
};
2.2 数组
对于数组,{}
是初始化数组元素的常用方式。
int arr[3] = {1, 2, 3}; // 初始化数组
或
int arr[]{1, 2, 3, 4, 5}; // 整数数组初始化
2.3 类和结构体
对于类和结构体,如果它们没有定义构造函数或者定义了接受 std::initializer_list
的构造函数,你可以使用 {}
进行初始化。
- 没有定义构造函数:这种情况下,类被视为聚合类型,可以直接使用
{}
初始化其成员。
struct Point {int x{};int y{};
};Point p = {1, 2}; // 初始化结构体
- 定义了接受
std::initializer_list
的构造函数:如果类定义了一个接受std::initializer_list
的构造函数,那么使用{}
会调用这个构造函数。
class MyClass {
public:MyClass(std::initializer_list<int> list) {// 处理 list}
};MyClass obj = {1, 2, 3, 4}; // 调用接受 std::initializer_list 的构造函数
2.4 容器初始化
C++11 中的标准容器(如std::vector
、std::list
等)也支持列表初始化。这使得容器可以像数组一样被初始化。例如:
std::vector<int> vec{1, 2, 3, 4, 5}; // 初始化整数向量vec
3 列表初始化的优势
-
防止窄化转换
窄化转换是指在类型转换时可能导致信息的损失或不准确的转换。列表初始化在初始化过程中会禁止窄化转换,从而提高了代码的类型安全性。例如:
int x{3.14}; // 编译错误:禁止从double到int的窄化转换
-
提高代码可读性
列表初始化使用统一的语法
{}
进行初始化,这使得代码更加简洁和一致。同时,它也避免了使用不同初始化方式时可能产生的混淆和错误。 -
支持聚合体初始化
在 C++11 中,聚合体的概念被扩展,它可以通过初始化列表进行初始化。这意味着符合聚合体条件的类型可以使用更灵活的初始化方式。例如:
struct Rectangle {int width{};int height{}; };Rectangle rect{10, 20}; // 使用聚合体初始化变量rect
4 注意事项
- 避免构造函数歧义:在某些情况下,使用
{}
可以避免由于多个构造函数导致的歧义。 - 窄化转换:对于可能导致数据丢失的窄化转换,使用
{}
可能会引发编译错误,从而提供更高的类型安全性。 - 默认构造函数:如果类没有定义默认构造函数且你尝试使用
{}
初始化其对象,而该类又不是聚合类型且没有接受std::initializer_list
的构造函数,那么这将导致编译错误。
5 示例代码
#include <iostream>
#include <vector>
#include <initializer_list>struct Point {int x, y;
};class MyClass {
public:MyClass(int a, int b) : a_(a), b_(b) {}MyClass(std::initializer_list<int> list) {// 假设我们只关心列表的第一个元素a_ = list.begin() != list.end() ? *list.begin() : 0;b_ = 0; // 其他元素被忽略}void print() const {std::cout << "a_ = " << a_ << ", b_ = " << b_ << std::endl;}
private:int a_, b_;
};int main() {Point p = {1, 2}; // 初始化结构体MyClass obj1(3, 4); // 使用构造函数初始化MyClass obj2 = {5}; // 使用接受 std::initializer_list 的构造函数初始化obj1.print(); // 输出: a_ = 3, b_ = 4obj2.print(); // 输出: a_ = 5, b_ = 0return 0;
}
在这个示例中,我们展示了如何使用 {}
初始化结构体、使用构造函数初始化类对象以及使用接受 std::initializer_list
的构造函数初始化类对象。
6 总结
C++11 中的列表初始化特性提供了一种更加简洁、直观和安全的初始化方式。它不仅统一了各种对象的初始化方式,而且还能在编译时捕捉窄化转换等潜在错误。因此,在 C++11 及之后的代码中,推荐使用列表初始化作为默认的初始化方式。通过深入理解并掌握这一特性,我们可以编写出更加健壮、可读和可维护的 C++ 代码。