
关注我,学习c++不迷路:
专栏如下:
后续会更新更多有趣的小知识,关注我带你遨游知识世界

期待你的关注。

文章目录
- [1. c++11的介绍:](#1. c++11的介绍:)
- [2. ``{}``初始化:](#2.
{}初始化:) -
- [2-1: 避免歧义和窄化转换问题:](#2-1: 避免歧义和窄化转换问题:)
- [2-2 统一的样式进行初始化:](#2-2 统一的样式进行初始化:)
- [2-3 对比两种初始化:](#2-3 对比两种初始化:)
- [2-4 陷阱:](#2-4 陷阱:)
- [3. 为什么要做到统一初始化?](#3. 为什么要做到统一初始化?)
-
- [1. **降低学习成本**](#1. 降低学习成本)
- [2. **提高代码可读性和一致性**](#2. 提高代码可读性和一致性)
- [3. **与现代C++理念一致**](#3. 与现代C++理念一致)
- 总结
1. c++11的介绍:
C++11是C++历史上一次里程碑式的更新,它为这门古老而强大的语言注入了新的活力。在C++11之前,我们习惯了繁琐的构造函数调用、复杂的初始化语法,以及在传递临时对象时的性能损耗。但C++11的到来,彻底改变了这一切。
想象一下,你可以用统一的{}语法初始化任何类型------从内置类型到自定义类,从数组到容器,代码变得更加简洁优雅。更令人兴奋的是,C++11引入了右值引用和移动语义,让编译器能够"偷走"临时对象的资源,避免不必要的拷贝,从而大幅提升程序性能。
本文将带你深入探索C++11的这两个核心特性:首先是列表初始化(统一初始化),它让C++的初始化语法"大一统";然后是右值引用与移动语义,这是C++11性能优化的利器。无论你是C++新手还是资深开发者,这些特性都将让你的代码更现代、更高效。
2. {}初始化:
在c++11没有出来之前,我们初始化无法做到统一,而前复杂,还很麻烦:

我们还发现,我们还可以这样初始化:

最后一个 a3 这个变量明显是错误的,这又他的精度会明显变小,于此同时在初始化STL容器的时候很麻烦,我们需要一个一个的进行插入。或者在自定义变量中,初始化也是很麻烦的事情。总之初始化变量,这个事情可谓是八仙过海各显神通。
cpp
// C++11之前,容器初始化很繁琐
std::vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
std::vector<int> v2(5, 1); // 5个元素,都是1
std::vector<int> v3(5); // 5个默认初始化的元素
// 对于map,更麻烦
std::map<int, std::string> m;
m[1] = "one";
m[2] = "two";
在引入c++11后:
2-1: 避免歧义和窄化转换问题:
原来的精度问题得到有效解决:

原本的警告直接变成error,这一点在编译器中我们也可以看到,{}初始化会禁止窄化转换,让代码更安全。
2-2 统一的样式进行初始化:
无论你是STL的vector还是map或者set,还是自定义类型Data或者student,我们都可以尝试使用{}来进行初始化:
cpp
int x = { 1 };
double y = { 1.2 };
vector<int> v({ 1,2,3,4,5,6 });
vector<int> v2 = { 1,2,3,4,5,6 };
map<string, int> mp = { {"1111",1},{"2222",2} };
这样看起来很是美观,这里是怎么做到的呢?这里其实引入了initializer_list。这是一个轻量级的容器:
cpp
// initializer_list是C++11引入的轻量级代理对象
namespace std {
template<typename T>
class initializer_list {
private:
const T* array; // 指向数组的指针
size_t len; // 元素个数
public:
// 构造函数等
};
}
我们拿vector中拿来作比较:我们在{}里面的数字先构成了initializer_list,随后调用vector的构造函数完成来构造:
cpp
template<typename T>
class vector {
public:
// 1. 默认构造
vector();
// 2. 指定大小和值
vector(size_t n, const T& value);
// 3. 迭代器范围
template<typename InputIt>
vector(InputIt first, InputIt last);
// 4. initializer_list构造(C++11新增)
vector(std::initializer_list<T> init);
// 5. 拷贝/移动构造
vector(const vector& other);
vector(vector&& other);
};
在这里我们可以看到很多构造函数。c++中总是调用自己最合适的函数:我们{}里面的变量已经构成了initializer_list,我们便可以调用4号来进行完成构造。
总结:
编译器背后做了什么:
- 创建临时数组:在栈上创建
int[5] = {1, 2, 3, 4, 5}。 - 创建
initializer_list:std::initializer_list<int> il(&临时数组[0], 5)。 - 调用匹配的构造函数:
vector(il)拷贝或移动这些元素到vector内部。
再来说说,map是怎么初始化的:
cpp
template<typename Key, typename Value>
class map {
public:
// 接受pair的initializer_list
map(std::initializer_list<std::pair<const Key, Value>> init);
};
cpp
std::map<int, std::string> m{{1, "one"}, {2, "two"}};
- 外层{}:识别为initializer_list的参数
- 内层{1, "one"}:解析为std::pair<int, std::string>的初始化
- pair的构造:std::pair<const int, std::string>(1, "one")
- 最终调用:map({pair1, pair2})
2-3 对比两种初始化:
我们来尝试多组对比:
cpp
vector<int> v(8, 2);
vector<int> v1{ 1,2,3,4,5,6,7,8 };
vector<int> v2 = { 1,2,3,4,5,6,7,8 };
vector<int> v3({ 1,2,3,4,5,6,7,8});
这几个初始化有什么区别呢:
- 第一个是传统的c++初始化,全部初始化成:2。只有在特定的情况下使用。
- 第二个则是c++11初始化的标准,我们会调用
vector(std::initializer_list<int> init)创建 8个元素,值分别为1到8。最推荐的现代C++写法。 - 第三个很像是拷贝列表初始化,在现代编译器中几乎和第二个相同,更像是赋值拷贝初始化。适用场景:v2更像"赋值"语义,v1更像"构造"语义。
- 第四个过于冗余了,显式创建一个
initializer_list<int>临时对象调用构造函数:vector(std::initializer_list<int> init)多此一举,但语法合法。
| 初始化方式 | 调用构造函数 | 创建元素数量 | 元素值 | 推荐度 |
|---|---|---|---|---|
v(8, 2) |
vector(size_t, const T&) |
8个 | 全是2 | ⭐⭐⭐(特定场景) |
v1{1,2,3,4,5,6,7,8} |
vector(initializer_list<T>) |
8个 | 1-8 | ⭐⭐⭐⭐⭐ |
v2 = {1,2,3,4,5,6,7,8} |
vector(initializer_list<T>) |
8个 | 1-8 | ⭐⭐⭐⭐ |
v3({1,2,3,4,5,6,7,8}) |
vector(initializer_list<T>) |
8个 | 1-8 | ⭐⭐ |
2-4 陷阱:
cpp
class Widget {
public:
Widget(int a, int b) {}
Widget(std::initializer_list<int> list) {}
};
Widget w1(1, 2); // 调用第一个构造函数
Widget w2{1, 2}; // 调用第二个构造函数(initializer_list)
Widget w3(1, 2, 3); // 错误!
Widget w4{1, 2, 3}; // 调用第二个构造函数
在这段函数中,总是调用最合适自己的,那么第一个就会调用第一个构造函数,第二个由于大括号会调用第二个构造函数。第三个由于无法匹配导致错误。
cpp
class MyClass {
int a;
std::string b;
public:
// 1. initializer_list构造(不常用)
MyClass(std::initializer_list<int> list) {
// 不推荐:类型不匹配
}
// 2. 多参数构造(推荐)
MyClass(int a, std::string b) : a(a), b(b) {}
// 3. 聚合类型(最简单)
// 去掉构造函数,让struct成为聚合类型
};
// 使用
MyClass obj{1, "hello"}; // 调用构造函数 #2
3. 为什么要做到统一初始化?
1. 降低学习成本
-
旧的C++:需要掌握多种初始化方式
int x = 0;(赋值)int x(0);(直接初始化)int arr[] = {1,2,3};(聚合初始化)std::vector<int> v(10, 1);(括号初始化)
-
新的C++:一种方式搞定所有
int x{0};int arr[]{1,2,3};std::vector<int> v{10, 1};
2. 提高代码可读性和一致性
cpp
// 以前,不同容器、不同类型的初始化语法不一致
int a = 1;
int b(2);
int c[] = {3, 4};
std::vector<int> v(5, 1); // 括号
std::map<int, int> m{{1,2}, {3,4}}; // 双括号
// 现在,统一用{},一眼就能看懂
int a{1};
int b{2};
int c[]{3, 4};
std::vector<int> v{5, 1}; // 注意:这里含义不同了!
std::map<int, int> m{{1,2}, {3,4}};
3. 与现代C++理念一致
C++11开始,C++致力于:
- 更安全(类型安全、转换安全)
- 更简洁(语法统一)
- 更现代 (符合现代编程语言趋势)
C++98/03的初始化语法是"拼凑"出来的,不同场景用不同语法。统一初始化让C++从"方言"走向"标准语"。
统一初始化为后续C++11/14/17/20的很多新特性提供了基础,比如: - 结构化绑定
- 模板参数推导
- 聚合初始化扩展
总结
统一初始化({})的核心价值:
- 安全:防止意外的类型转换
- 统一:一种语法,多种场景
- 简洁:减少记忆负担
- 现代:让C++更符合现代语言标准
正如C++之父Bjarne Stroustrup所说:"我希望C++能有一种统一的初始化方式,让初始化不再令人困惑。" {}就是这个愿景的实现。