深入浅出C++右值及Move语义

Move是c++ 11中加入的特性,本文力图通过简单易懂的方式解释什么是Move以及我们为什么需要Move。

什么是右值

int x = 3;

在这个表达式中,x是左值,3是右值。具体什么意思呢,左值是有内存地址的,可以在之后的代码中继续使用的值;而右值是临时的,系统没有分配地址的,使用完就消亡的值。继续看下面的代码:

int a = 0;
int b = 0;
int x = a + b;

在这段代码中,a, b, x都是左值,而a+b是右值,系统并没有为a+b分配地址,a+b在被使用过后即消亡。总之右值差不多就是阅后即焚的意思。

什么是右值引用

顾名思义右值引用即是一个对右值的引用。为什么会用到右值引用呢?假设有这样一个函数:

1 void Foo(Bar b); //传值
2 void Foo(Bar* pb); //传指针
3 void Foo(Bar& rb); //传引用
4 void Foo(Bar&& rrb); //传右值引用

我们知道类似1这样的值传递会调用Bar类的拷贝构造函数来得到一个b的拷贝,如果Bar是一个很占资源的类,完完全全的拷贝一个对象是效率很低的方式。通常在这种情况下我们会传指针或者引用。但如果b只是一个临时变量(比如一个临时字符串)我们并不想分配一个指针或变量给它,这个时候如果Foo函数提供了接受右值引用的接口就不一样了,我们可以大大方方的传值而不必担心我们会对参数进行拷贝,取而代之的是编译器会执行Bar类的Move构造函数。事实上c++ 11也提供了T&&的推导规则,即右值实参为右值引用,左值实参仍为左值引用。

什么是Move构造函数/Move语义

Bar::Bar(Bar&& bla):data(std::move(bla.data))
{
}

假设Bar类有一种资源data,一旦执行了Move构造函数就相当于把data易了主,原先的拥有者bla变成了当前对象。注意我们并没有另外申请空间创造一个新的data而只是进行了一个类似指针移动的操作。为什么可以这样搞?因为bla是个右值引用,而这个右值马上就消亡了,所以它的东西不用白不用。回到上面说的,当函数接受右值参数后调用了参数的Move构造函数,就可以无缝地在原来的资源上进行操作。实际工作中,经常会碰到有一些简单的类,我们不想单独开辟堆上的空间来保存,但又不想总是通过值传递来通信,那么就可以用右值引用来传递。

Bar bar(data);
Foo(std::move(bar));

这里std::move()可以手动把bar转换成右值。

猜你喜欢

转载自www.cnblogs.com/north212/p/10487178.html