学习C++右值引用

值类型

每一个C++表达式都有一个 “值类型”——它是编译器在表达式计算过程中创建、复制和移动临时对象时必须遵循的规则的基础。例如:

  • “=”左边的值就是 “左值(lvalue)”,计算时其地址发挥了作用。
  • “=”右边的值就是 “右值(rvalue)”,计算时其数据发挥了作用。

除此之外还有其他的 “值类型”,他们之间的关系如下:
在这里插入图片描述
详见Value Categories: Lvalues and Rvalues (C++) | Microsoft Docs

左值引用

“右值引用” 出现之前,只有一种“引用”,和“指针”的性质类似,实际掌握了所引用变量的地址,例如:

#include<iostream>
using namespace std;

int main()
{
	int x = 3;
	int& r = x;
	r = 7;
	cout << x << endl;
}

这里rx的引用,因此让它的值改变等价于让x的值改变。
“右值引用” 出现之后,上面这种引用被称为 “左值引用”,因为它所指代的是一个可被用户改变的“左值”。
Lvalue 引用声明符: & | Microsoft Docs

右值引用想要解决的问题

假设有一个类class TestObject
情况(1)

int main()
{
	TestObject a;
	TestObject b = a;
}

情况(2)

TestObject GetObject()
{
	TestObject t = TestObject();
	return t;
}
int main()
{
	TestObject b = GetObject();
	return 0;
}

假如不存在 “右值引用”,那么考虑b在构造时,情况(1)和(2)是对其是等价的,都会调用“拷贝构造函数”。然而实际上,情况(1)和(2)是不同的:

  • 在情况(1)中,是用a来对b构造,而a是另一个用户可访问的对象。
  • 在情况(2)中,是用GetObject()返回的对象来对b构造,而GetObject()返回的对象实际是一个临时对象,是用户不可访问的。

可见假如不存在 “右值引用”,则上述两者情况是无法区分的。而如果能区分,则会让一些性能优化成为可能。

右值引用

“右值引用” 使用&&来表示。
有了它,便可以定义移动构造函数和移动赋值运算符。例如TestObject定义:

class TestObject
{
public:
	TestObject()
	{
		cout << "构造函数" << endl;
	}
	TestObject(TestObject& Ohter)
	{
		cout << "拷贝构造函数" << endl;
	}
	TestObject(TestObject&& Ohter)
	{
		cout << "移动构造函数" << endl;
	}
};

这样再回头看刚才的那两种情况:
情况(1)输出:

构造函数
拷贝构造函数

情况(2)输出:

构造函数
移动构造函数

可以看到二者针对不同的情况可以得到区别。

关于右值引用的官方说明详见:
Rvalue Reference Declarator: && | Microsoft Docs

使用移动构造函数优化

正如官方文档所说:利用移动语义,你可以编写将资源(如动态分配的内存)从一个对象转移到另一个对象的代码
例如TestObject有一个数据data,那在拷贝构造函数中和移动构造函数中对其的处理应该是不同的:

class TestObject
{
	int* data;
public:
	TestObject()
	{
		cout << "构造函数" << endl;
		data = new int[100];
	}
	TestObject(TestObject& Other)
	{
		cout << "拷贝构造函数" << endl;
		memcpy(Other.data, data, 100 * sizeof(int));
	}
	TestObject(TestObject&& Other)
	{
		cout << "移动构造函数" << endl;
		delete[] data;
		data = Other.data;
		Other.data = nullptr;
	}
};
  • 拷贝构造函数中应该是真的将data复制出一份新的
  • 移动构造函数只是将另一个对象的指针拿来。这不会造成问题,因为另一个对象本身就是临时对象。

std::move

无条件将其自变量强制转换为右值引用。

template <class Type>
    constexpr typename remove_reference<Type>::type&& move(Type&& Arg) noexcept;

例如:

int main()
{
	TestObject a;
	TestObject b = std::move(a);
}

将会输出:

构造函数
移动构造函数

当然,使用了std::move也就意味着变量被转移,那么它就不应该在接下来继续使用了。

参考资料

有些的理解可能不准确。下面是一些参考资料:

权威的官方文档:
右值引用 | Microsoft Docs

看起来比较权威的资料:
A Brief Introduction to Rvalue References

其他的讨论:
C++ 11的移动语义 - 行者孙 - 博客园
C++ 11 中的右值引用 - 天方 - 博客园

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值