原文:http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html
先看看这个例子:
#include <iostream>
using namespace std;
vector<int> doubleValues (const vector<int>& v)
{
vector<int> new_values( v.size() );
for (auto itr = new_values.begin(), end_itr = new_values.end(); itr != end_itr; ++itr )
{
new_values.push_back( 2 * *itr );
}
return new_values;
}
int main()
{
vector<int> v;
for ( int i = 0; i < 100; i++ )
{
v.push_back( i );
}
v = doubleValues( v );
}
调用函数doubleValues时是有两次复制的,一次是在函数返回时,因为local变量要被回收,所以需要copy构造一个临时对象来返回,当返回这个临时对象后,又需要一次copy赋值操作来赋值给v。你可能会说,不返回对象就可以啊,可以返回一个引用或者一个指针,但是要这样做就得分配内存,但是C++的一个设计目标就是尽量少分配内存。上述更糟糕之处在于,返回的临时对象使用完之后是会被销毁掉的。
理论上来说,在内存中我们完全可以把临时对象的指针偷过来,而不去拷贝。还有就是我们为什么不能move呢?C++03说,我不知道哪个对象是临时的,哪个不是临时的,但是C++11却可以做到。
这就是右值引用和move语义要做的事情:move语义可以使你在使用临时对象时避免拷贝,并且可以安全的使用临时对象里的资源。
左值和右值
C++中,左值是指可以使用其地址的表达式,左值提供一种(半)永久的内存,比如:
int a;
a = 1; //a是一个左值
又如:
intx;
int& getRef ()
{
returnx;
}
getRef()
= 4;//getRef()这个表达式也是一个左值,因为其提供的返回值全局变量,是有固定地址的。
intx;
intgetVal ()
{
returnx;
}
getVal();
string getName ()
{
return"Alex";
}
getName();
string
nema = getName();
const
string&
name = getName();
//
ok
string&
name = getName();
//
NOT ok
C++11中,会让你可以绑定一个mutable右值引用,也就是说,一个值是否临时的,可以使用右值引用来检测,右值引用使用&&语法,可以是const也可以是非const的:
const
string&&
name = getName();
//
ok
string&&
name = getName();
//
also ok - praise be!
printReference (constString& str)
{
cout << str;
}
printReference (String&& str)
{
cout << str;
}
string me( "alex");//mutable rvalue-references类型
printReference( me ); // calls the first printReference function, taking an lvalue reference
现在,右值引用版本的函数就像一个俱乐部的入口,只有临时变量才可以进入。
现在,既然有方法检测出是否临时变量了,有什么用处呢?
class ArrayWrapper
{
public:
ArrayWrapper (int n)
: _p_vals( new int[ n ] )
, _size( n )
{}
// copy constructor
ArrayWrapper (const ArrayWrapper& other)
: _p_vals( new int[ other._size ] )
, _size( other._size )
{
for ( int i = 0; i < _size; ++i )
{
_p_vals[ i ] = other._p_vals[ i ];
}
}
~ArrayWrapper ()
{
delete [] _p_vals;
}
private:
int *_p_vals;
int _size;
};
来看move构造:class ArrayWrapper
{
public:
// default constructor produces a moderately sized array
ArrayWrapper ()
: _p_vals( new int[ 64 ] )
, _size( 64 )
{}
ArrayWrapper (int n)
: _p_vals( new int[ n ] )
, _size( n )
{}
// move constructor
ArrayWrapper (ArrayWrapper&& other)
: _p_vals( other._p_vals )
, _size( other._size )
{
other._p_vals = NULL;
}
// copy constructor
ArrayWrapper (const ArrayWrapper& other)
: _p_vals( new int[ other._size ] )
, _size( other._size )
{
for ( int i = 0; i < _size; ++i )
{
_p_vals[ i ] = other._p_vals[ i ];
}
}
~ArrayWrapper ()
{
delete [] _p_vals;
}
private:
int *_p_vals;
int _size;
};
可以看出,move构造和copy构造相互重载了,那么调用的时候如何判断调用哪个构造呢?很简单,上边不是已经有了方法吗,如果是临时对象就会自动调用move构造,如果不是,就会调用copy构造。