目录
前言
Part1. 统一列表初始化
Part1.1. 核心作用:一套语法搞定所有类型初始化
Part1.2. 内置基础类型使用
Part1.3. 自定义结构体/类使用
Part1.4. 禁止窄转换
Part1.5. STL 容器与列表初始化
Part2. 左值、右值、左值引用、右值引用基础概念
Part2.1. 左值 and 右值
Part2.1.1. 左值 lvalue
Part2.1.2. 右值 rvalue
Part2.2. 引用分类规则
Part2.3. std::move 底层原理
Part2.4. 右值引用两大核心价值
Part3. 移动语义:移动构造、移动赋值(性能优化核心)
Part3.1. 为什么需要移动语义?
Part3.2. 移动构造函数
Part3.3. 返回值优化配合移动
Part3.4. 移动赋值运算符
Part3.4. swap 在移动语义中的关键作用
Part4. 结语
前言
C++11 是现代 C++ 的分水岭,引入大量语法与语义优化,其中统一列表初始化、右值引用、移动语义三块是重中之重,我们来看看吧。
let's go!!!!!!!!
Part1. 统一列表初始化
Part1.1. 核心作用:一套语法搞定所有类型初始化
C++11 引入花括号 {} 统一初始化规则,内置类型、自定义类、STL 容器全部通用,消除老式初始化语法歧义。
Part1.2. 内置基础类型使用
int x = 3;
int x{3}; // C++11 列表初始化写法
用码道免费领 1 个月 Token
cpp
运行
Part1.3. 自定义结构体/类使用
struct Date {
int year, month, day;
};
Date d1 = {2025, 1, 1};
Date d2{2025, 1, 1};
用码道免费领 1 个月 Token
cpp
运行
Part1.4. 禁止窄转换
老式赋值 = 允许精度丢失,花括号初始化直接编译报错:
int x{3.14}; // 编译失败:double→int 窄转换不允许
int x = 3.14;// 老式语法静默截断,隐患极大
用码道免费领 1 个月 Token
cpp
运行
Part1.5. STL 容器与列表初始化
vector、map、string 等标准容器重载了接收 initializer_list 的构造函数与赋值运算符:
vector<int> aaa{1,2,3};
map<int, string> mp{{1, "a"}, {2, "b"}};
用码道免费领 1 个月 Token
cpp
运行
底层原理:编译器会把 {...} 隐式转为 std::initializer_list<T> 临时对象,容器遍历该对象完成批量初始化。
Part2. 左值、右值、左值引用、右值引用基础概念
Part2.1. 左值 and 右值
Part2.1.1. 左值 lvalue
<1> 定义:拥有持久内存地址、生命周期可延长、能取地址的表达式。
<2> 典型:普通变量、数组元素、返回普通引用的函数。
<3> 特征:&变量 合法,内存长期存在。
string s = "test";
cout << &s; // 合法,s 是左值
用码道免费领 1 个月 Token
cpp
运行
Part2.1.2. 右值 rvalue
<1> 定义:临时对象、字面常量、运算中间结果,仅临时存在栈/寄存器,不能取地址。
<2> 两类:纯右值(字面量10、"abc"、表达式a+b)、将亡值(std::move转换后的对象)。
<3> 特征:生命周期极短,语句结束立刻销毁。
&(1 + 2); // 编译报错,字面表达式是右值,不可取地址
&string("111"); // 报错,临时字符串是右值
用码道免费领 1 个月 Token
cpp
运行
Part2.2. 引用分类规则
<1>普通左值引用 T&
只能绑定左值,无法绑定临时右值:
int& r = 10; // 报错,10是右值,不能绑定普通左值引用
用码道免费领 1 个月 Token
cpp
运行
<2> const 左值引用 const T&
万能引用,既能绑定左值,也能绑定右值,是函数形参最常用写法,既能接收普通变量,也能接收临时对象。
void func(const string& s);
func("hello"); // 合法,字面右值绑定const左值引用
用码道免费领 1 个月 Token
cpp
运行
<3> 右值引用 T&&
仅能绑定右值/经move转换后的将亡值,核心用于实现移动语义:
int&& aaa = (int)10; // 字面常量右值绑定右值引用
string&& s2 = move(s1); // move把左值转为将亡值,绑定右值引用
用码道免费领 1 个月 Token
cpp
运行
Part2.3. std::move 底层原理
std::move 只是类型转换模板,不移动任何内存、不产生拷贝,仅强制把左值转为右值类型
转换后原变量所有权可以被转移,实现零拷贝。
Part2.4. 右值引用两大核心价值
<1> 延长临时对象生命周期
普通临时右值语句结束直接销毁,绑定右值引用后,生命周期延长至引用变量作用域结束:
// s1+s2 是临时右值,正常本行结束销毁
string&& temp = s1 + s2;
// temp 可以全程使用,不会提前析构
用码道免费领 1 个月 Token
cpp
运行
<2> 函数重载精准匹配
函数形参区分 T&、const T&、T&&,编译器自动匹配最优重载:
• 传入普通左值 → 匹配 T&
• 传入常量/临时右值 → 匹配 const T&
• 传入move后的将亡值 → 匹配 T&&
Part3. 移动语义:移动构造、移动赋值(性能优化核心)
Part3.1. 为什么需要移动语义?
<1> 传统拷贝构造:完整复制堆内存资源(string字符数组、vector堆数组),临时对象拷贝存在大量无意义内存复制,性能损耗严重。
<2> 移动语义:直接转移临时对象的堆资源所有权,不拷贝内存,仅交换内部指针,时间复杂度 O(1)。
Part3.2. 移动构造函数
函数参数为右值引用 T&&,接管传入临时对象的资源,原对象置空
class string {
public:
// 拷贝构造(深拷贝,开销大)
string(const string& s);
// 移动构造(仅转移指针,零拷贝)
string(string&& s) {
swap(s); // 交换内部缓冲区,资源直接转移
}
};
//调用场景:用临时对象初始化新变量
string s1("111");
string s2 = move(s1);
// move(s1)转为右值,触发移动构造,堆内存直接转移给s2,注意s1的资源没了!!
用码道免费领 1 个月 Token
cpp
运行
Part3.3. 返回值优化配合移动
1. 老式无移动语义:函数返回局部对象,会执行拷贝构造,产生两次堆内存复制;
2. C++11 移动语义:return 局部对象会被识别为右值,优先触发移动构造;
3. 编译器优化:直接省略移动,原地构造对象,零拷贝。
string get_str() {
string res = "test";
return res; // res视为右值,优先移动,编译器可直接优化原地构造
}
string s = get_str();
用码道免费领 1 个月 Token
cpp
运行
Part3.4. 移动赋值运算符
//重载 operator=(T&&),用于临时对象赋值给已有变量:
string& operator=(string&& s) {
swap(s);
return *this;
}
用码道免费领 1 个月 Token
cpp
运行
三种赋值对比:
<1> 拷贝赋值 T& operator=(const T&):深拷贝堆内存,开销大;
<2> 移动赋值 T& operator=(T&&):交换指针,无内存拷贝,性能极高;
<3> const引用赋值:无法修改原临时对象,只能拷贝。
Part3.4. swap 在移动语义中的关键作用
移动构造/移动赋值内部统一使用swap有巨大优势:
<1> 无内存分配、无拷贝,仅交换内部指针、长度、容量成员;
<2> 自动处理原对象残留资源,无需手动释放,不会内存泄漏;
<3> 同时兼容左值、右值场景,代码统一简洁。
Part4. 结语
这篇文章我们认识到了C++11相关知识,接下来,小编还会带来更多关于C++11的知识,敬请期待~
最后,祝大家可以:春风得意马蹄疾,一日看尽长安花!
最后的最后,要是觉得本文还可以的话,可以点点赞,关注小编一波,谢谢大家!~