【重学C++】04 | 说透C++右值引用、移动语义、完美转发(上)
文章首发【重学C++】04 | 说透C++右值引用、移动语义、完美转发(上)
引言
大家好,我是只讲技术干货的会玩code,今天是【重学C++】的第四讲,在前面《03 | 手撸C++智能指针实战教程》中,我们或多或少接触了右值引用和移动的一些用法。
右值引用是 C++11 标准中一个很重要的特性。第一次接触时,可能会很乱,不清楚它们的目的是什么或者它们解决了什么问题。接下来两节课,我们详细讲讲右值引用及其相关应用。内容很干,注意收藏!
左值 vs 右值
简单来说,左值是指可以使用&符号获取到内存地址的表达式,一般出现在赋值语句的左边,比如变量、数组元素和指针等。
int i = 42;
i = 43; // ok, i是一个左值
int* p = &i; // ok, i是一个左值,可以通过&符号获取内存地址
int& lfoo() { // 返回了一个引用,所以lfoo()返回值是一个左值
int a = 1;
return a;
};
lfoo() = 42; // ok, lfoo() 是一个左值
int* p1 = &lfoo(); // ok, lfoo()是一个左值相反,右值是指无法获取到内存地址的表达是,一般出现在赋值语句的右边。常见的有字面值常量、表达式结果、临时对象等。
int rfoo() { // 返回了一个int类型的临时对象,所以rfoo()返回值是一个右值
return 5;
};
int j = 0;
j = 42; // ok, 42是一个右值
j = rfoo(); // ok, rfoo()是右值
int* p2 = &rfoo(); // error, rfoo()是右值,无法获取内存地址左值引用 vs 右值引用
C++中的引用是一种别名,可以通过一个变量名访问另一个变量的值。
https://huiwan-images-1253247883.cos.ap-guangzhou.myqcloud.com/cpp/20230512160839.png
上图中,变量a和变量b指向同一块内存地址,也可以说变量a是变量b的别名。
在C++中,引用分为左值引用和右值引用两种类型。左值引用是指对左值进行引用的引用类型,通常使用&符号定义;右值引用是指对右值进行引用的引用类型,通常使用&&符号定义。
class X {...};
// 接收一个左值引用
void foo(X& x);
// 接收一个右值引用
void foo(X&& x);
X x;
foo(x); // 传入参数为左值,调用foo(X&);
X bar();
foo(bar()); // 传入参数为右值,调用foo(X&&);所以,通过重载左值引用和右值引用两种函数版本,满足在传入左值和右值时触发不同的函数分支。
值得注意的是,void foo(const X& x);同时接受左值和右值传参。
void foo(const X& x);
X x;
foo(x); // ok, foo(const X& x)能够接收左值传参
X bar();
foo(bar()); // ok, foo(const X& x)能够接收右值传参
// 新增右值引用版本
void foo(X&& x);
foo(bar()); // ok, 精准匹配调用foo(X&& x)到此,我们先简单对右值和右值引用做个小结:
[*]像字面值常量、表达式结果、临时对象等这类无法通过&符号获取变量内存地址的,称为右值。
[*]右值引用是一种引用类型,表示对右值进行引用,通常使用&&符号定义。
右值引用主要解决一下两个问题:
[*]实现移动语义
[*]实现完美转发
这一节我们先详细讲讲右值是如何实现移动效果的,以及相关的注意事项。完美转发篇幅有点多,我们留到下节讲。
复制 vs 移动
假设有一个自定义类X,该类包含一个指针成员变量,该指针指向另一个自定义类对象。假设O占用了很大内存,创建/复制O对象需要较大成本。
class O {public: O() { std::cout
页:
[1]