C++11——3.17-3.20 右值引用
★★★原文链接★★★:https://subingwen.cn/cpp/rvalue-reference/
- 3.17. 右值和右值引用
- 3.18. 右值引用的作用以及使用
- 3.19. 未定引用类型的推导
- 3.20. 右值引用的传递
- 3.17. 右值和右值引用
左值,lvalue,locator value,(locator:定位器)
右值,rvalue,read value,(read:只读)
右值分为 纯右值 和 将亡值。
右值语法:
int&& a = 10;
两个&
能取地址是左值,不能取地址是右值
不能用左值去初始化右值引用,要用右值初始化右值引用
// 左值
int num = 9; // num是左值
// 左值引用
int& a = num; // a是左值引用,不占用额外的内存地址,是num的别名
// 右值
// 常量整数,常量字符串等...55, abcd,不能取地址,只能读它的内容
// 右值引用
int&& b = 8; // ★★★b是右值引用,需要用右值初始化(8)
// int&& b2 = b; // ★★★报错,不能用 右值引用 初始化 右值引用
// 常量左值引用
const int& c = num;
const int& c2 = b; // 可以用 右值引用 初始化 常量左值引用
const int& c3 = 1;
const int& c4 = a;
// 常量右值引用
const int&& d = 6; // 常量右值引用 的初始化也需要用右值(6)
// 常量左值引用 可以使用 常量右值引用 来初始化
const int& e = d;
总结:
常量左值引用 可以用 左值、左值引用、右值、右值引用、常量右值引用 来进行初始化。
- 3.18. 右值引用的作用以及使用
先来看一个例子:
#include <iostream>
using namespace std;
class Test {
public:
// 默认构造函数
Test() :m_num(new int(100))
{
cout << "construct: my name is jerry" << endl;
cout << "m_num地址:" << &m_num << endl;
}
// 拷贝构造函数
Test(const Test& a) :m_num(new int(*a.m_num))
{
cout << "copy construct: my name is tom" << endl;
}
// 析构
~Test() {
cout << "destruct Test class..." << endl;
delete m_num;
}
int* m_num;
};
Test getObj() {
Test t;
return t;
}
int main() {
Test a = getObj();
return 0;
}
res:
可以看到 a 调用 getObj() 时,先发生了一次默认构造,此时构造的是 getObj() 函数中的 t;然后发生了一次拷贝构造,此时 t 拷贝给 a;getObj() 函数调用完成后析构掉 t,主函数运行结束析构掉 a.
给上面的代码加一个 移动构造函数(在 class 内加):
(右值引用构造函数 也称为 移动构造函数)
// 移动构造函数 作用:★★★复用其他对象中的资源(具体是指其他对象中的 ★★★堆内存)
// m_num,转移堆内存资源,浅拷贝
Test(Test&& a):m_num(a.m_num)
{
a.m_num = nullptr; // ★★★因为此对象的 m_num 中存放的地址和 a 对象的 m_num 存放的地址相同,所以 a 对象被析构时 不能让 a.m_num 指向的地址的内存被释放,所以把 a.m_num 置空
cout << "move construct..." << endl;
}
res:
可以看到 a 调用 getObj() 时,发生了一次默认构造,一次移动构造;
如果类中有移动构造函数,在进行赋值操作时,编译器会判断 = 右边的对象是不是临时对象:
= 右边是临时对象(将亡值):调用移动构造函数;= 右边不是临时对象,调用拷贝构造函数;如果没有移动构造函数,调用拷贝构造函数。
调用 移动构造时,没有进行堆内存资源的申请(拷贝构造函数 有堆内存资源的申请,深拷贝),移动构造函数 实际上进行了一个浅拷贝,实现了 堆内存资源的转移,提高了效率。
- 3.19. 未定引用类型的推导
记住两点即可:
- 通过右值推导 T&& 或者 auto&& 得到的是一个右值引用类型;
通过非右值(右值引用、左值、左值引用、常量右值引用、常量左值引用)推导 T&& 或者 auto&& 得到的是一个左值引用类型。 - const T&& 表示一个右值引用,不是未定引用类型。
- 3.20 右值引用的传递
看一段代码:
#include <iostream>
using namespace std;
void printValue(int &i)
{
cout << "l-value: " << i << endl;
}
void printValue(int &&i)
{
cout << "r-value: " << i << endl;
}
void forward(int &&k)
{
printValue(k);
}
int main()
{
int i = 520;
printValue(i);
printValue(1314);
forward(250);
return 0;
}
res:
编译器会根据传入的参数的类型(左值还是右值)调用对应的重置函数(printValue),函数forward()接收的是一个右值,但是在这个函数中调用函数printValue()时,参数k变成了一个命名对象,编译器会将其当做左值来处理。
总结:
- 右值引用类型可能是左值也可能是右值
- 编译器会将已命名的右值引用视为左值,将未命名的右值引用视为右值
★★★原文链接★★★:https://subingwen.cn/cpp/rvalue-reference/
(〃>_<;〃)(〃>_<;〃)(〃>_<;〃)