目录
前言
Part1. 临时对象生命周期延长规则
Part1.1. 核心结论
Part1.2. 左右值引用统一设计目的
Part1.3. 左值引用两大硬性限制
Part1.4. 函数不能返回局部对象引用的替代方案
Part2. 移动构造、移动赋值
Part2.1. 底层本质
Part2.2. 标准移动赋值运算符实现
Part2.3. 移动语义性能收益对比
Part3. 值分类体系
Part3.1. 纯右值 prvalue
Part3.2. 将亡值 xvalue
Part4. 引用折叠
Part4.1. 折叠规则
Part4.2. 模板万能引用 T&& 推导逻辑
Part5. 完美转发 std::forward 原理与实战
Part5.1. forward 作用
Part5.2. STL list::push_back 双版本底层逻辑
Part5.3. 统一万能转发版 push_back
Part5.4. 链表节点 list_node 构造坑点
Part6. 结语
前言
C++11 核心:生命周期延长规则、左右值引用作用、引用折叠、万能引用/完美转发、容器 emplace/push_back 底层实现、移动构造赋值设计坑点等等,我们来看看吧。
let's go!!!!!!!!
Part1. 临时对象生命周期延长规则
Part1.1. 核心结论
<1> 绑定引用不会改变临时对象原本的内存位置,仅延长它的生命周期;
<2> 临时对象原本出当前语句就销毁,若用 const T& 左值引用接收,生命周期延长至引用变量作用域结束。
string get_temp() { return "abc"; }
// get_temp() 返回纯右值临时
const string& ref = get_temp();
// 原本临时语句结束销毁,绑定const&后,ref作用域内临时存活
cout << ref; // 合法,不会野引用崩溃
//注意:普通左值引用 T& 不能绑定临时右值,只有 const 左值引用、右值引用 T&& 可以绑定临时并延长生命周期。
用码道免费领 1 个月 Token
cpp
运行
Part1.2. 左右值引用统一设计目的
所有引用(左值/右值引用)底层目标一致:减少对象拷贝,提升运行效率。
1. 左值引用 T&:既能避免拷贝,还可在函数内修改实参、作为链式返回值,使用场景更广
2. 右值引用 T&&:专门接管临时对象资源,实现移动语义,彻底消除堆内存深拷贝开销。
Part1.3. 左值引用两大硬性限制
<1> 不能绑定临时右值:T& a = string("123"); 编译报错;
<2> 禁止返回局部变量左值引用:函数内局部对象栈内存,函数结束销毁,返回 T& 会产生悬空野引用,未定义行为。
Part1.4. 函数不能返回局部对象引用的替代方案
需求:函数内修改临时/局部变量,又不能返回引用、只能值返回时,解决方案:
需要修改的数据放入函数入参(引用/指针),函数返回值设为 void,直接修改外部传入的实参,规避拷贝与悬空引用双重问题。
// 错误:返回局部左值引用,悬空
string& bad_func() {
string s = "test";
return s;
}
// 正确:外部传入引用修改,无返回值
void good_func(string& out) {
out = "test";
}
用码道免费领 1 个月 Token
cpp
运行
Part2. 移动构造、移动赋值
Part2.1. 底层本质
移动语义 = 资源所有权转移,不拷贝堆内存、不分配新内存,仅交换对象内部指针,时间复杂度 O(1);
编译器会做返回值优化,原本两次拷贝直接优化为一次原地构造,再结合移动进一步消除开销。
Part2.2. 标准移动赋值运算符实现
string& operator=(string&& s) {
swap(s); // 交换内部缓冲区、长度、容量
return *this; // 返回左值引用,支持链式赋值 b = c = move(str)
}
//参数 string&& s:接收将亡值/纯右值;
//返回 string& 左值引用,保证链式赋值语法正常;
//内部 swap 无内存分配,不会内存泄漏,自动释放当前对象旧资源。
用码道免费领 1 个月 Token
cpp
运行
Part2.3. 移动语义性能对比
<1> 无移动:拷贝构造/拷贝赋值 = 堆内存完整复制 + 析构销毁旧临时,双重开销;
<2> 有移动:移动构造/移动赋值 = 仅交换指针,无内存拷贝,性能大幅提升;
适用场景:管理堆资源的类(string、vector、list、自定义链表节点)。
Part3. 值分类体系
广义右值 = 纯右值(prvalue) + 将亡值(xvalue)
Part3.1. 纯右值
字面量、运算临时、无名临时对象(100、"hello"、string("temp")、s1 + s2),无底层原始具名变量,语句结束立刻销毁。
Part3.2. 将亡值
可以被移动掠夺资源的对象,来源:
<1> std::move(左值变量);
<2> 返回 T&& 右值引用的函数返回值;
背后绑定真实存在的左值变量,只是被标记“可转移资源”。
Part4. 引用折叠
Part4.1. 折叠规则
只有内外两层都是右值引用 &&,折叠结果才是 &&;其余所有组合,最终都折叠为普通左值引用 &
原始类型组合 折叠后结果
<1> T& & → T&
<2> T& && → T&
<3> T&& & → T&
<4> T&& && → T&&
Part4.2. 模板万能引用推导逻辑
模板 template<class T> void f(T&& t),T&& 被称为万能引用/转发引用,根据传入实参类型自动推导 T,并触发引用折叠:
<1> 传入纯右值 f(10)
实参是右值,推导 T = int,参数类型 int&&(右值引用)
<2> 传入普通左值变量 int a=0; f(a)
实参是左值,推导 T = int&,折叠后参数 int&(左值引用)
<3> 传入 const 左值 const int a=0; f(a)
推导 T = const int&,折叠后 const int&
<4> 传入 move 后的将亡值 f(move(b))
实参为将亡值(广义右值),推导 T=int,参数 int&&
万能引用本质:编译器自动推导 T + 引用折叠,一套参数同时兼容左值、右值。
Part5. 完美转发 std::forward 原理与实战
Part5.1. forward 作用
保持传入参数原本的值类别(左值/右值),原样转发给下层函数,不丢失左右值属性。
template<class T>
void f(T&& t) {
Fun(forward<T>(t));
}
用码道免费领 1 个月 Token
cpp
运行
<1> 若 t 原本是左值:forward<T>(t) 产出左值;
<2> 若 t 原本是右值/将亡值:forward<T>(t) 产出右值;
如果直接传 t,具名引用变量本身是左值,会丢失右值属性,无法触发移动语义。
Part5.2. push_back 双版本底层逻辑
list 的 push_back 重载两套接口,性能差异巨大:
<1> push_back(const T& val):接收左值,触发拷贝构造,堆内存完整复制;
<2> push_back(T&& val):接收右值,触发移动构造,仅转移资源,性能远高于左值版本。
Part5.3. 万能转发版 push_back
//不分开写两个重载,用万能引用+forward 一套代码兼容左右值:
template<class X>
void push_back(X&& x) {
insert(end(), forward<X>(x));
}
//insert 内部构造 list_node,forward<X>(x) 保留 x 原始左右值,自动匹配移动/拷贝构造。
用码道免费领 1 个月 Token
cpp
运行
Part5.4. 链表节点 list_node 构造坑点
//错误写法:
template<class X>
list_node(X&& data = T{})
: _data(forward<T>(data))
{}
用码道免费领 1 个月 Token
cpp
运行
三大缺陷
<1> 默认参数无法推导模板 X
不传参 list_node<int> node; 时,仅靠默认值 T{} 不能驱动模板参数 X 推导,直接编译报错;
<2> forward 模板参数写错
必须写 forward<X>(data),不能写 forward<T>(data);写死 T 会强制转换类型,破坏完美转发;
<3> 万能模板构造劫持拷贝构造
无约束 X&& 会匹配 list_node 自身对象,调用拷贝时优先匹配模板构造,覆盖自定义拷贝构造函数,逻辑二义性。
//正确拆分方案:分离无参构造与转发构造
// 单独无参构造,处理默认空节点,无需模板推导
list_node() : _data(T{}), _next(nullptr), _prev(nullptr) {}
// 万能转发构造,无默认参数,模板推导正常
template<class X>
list_node(X&& data)
: _data(std::forward<X>(data)), _next(nullptr), _prev(nullptr)
{}
用码道免费领 1 个月 Token
cpp
运行
Part6. 结语
这篇文章我们认识到了C++11相关知识,接下来,小编还会带来更多关于C++11的知识,敬请期待~
最后,祝大家可以:春风得意马蹄疾,一日看尽长安花!
最后的最后,要是觉得本文还可以的话,可以点点赞,关注小编一波,谢谢大家!~