PS: 通过引入接收右值的函数形参,可以通过接收右值来实现高效
PS在C++11中,标准库在<utility>中提供了一个有用的函数std::move,这个函数的名字具有迷惑性,因为实际上 std::move并不能移动任何东西,它唯一的功能是将一个左值强制转化为右值引用,继而我们可以通过右值引用使用该值,以用于移动语义。从实现上 讲,std::move基本等同于一个类型转换:static_cast<T&&>(lvalue);
C++ 11带来了move语义,可以有效的提高STL的效率,这篇文章写的非常好,可以参考,这里对原文进行翻译,加入我自己的理解
原文: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
name = getName();
此时,对“Alex”进行隐式类型转换,转换为一个string的临时对象,然后拷贝构造给name变量
上边说到C++11能检测出来临时变量和非临时变量,现在就来看看怎么检测
既然有的表达式返回临时变量,那么如果对表达式重载,那么一个返回临时变量的表达式和返回非临时变量的表达式是否有哪些不同呢?
const
string&
name = getName();
//
ok
string&
name = getName();
//
NOT ok
为什么第一个ok第二个不ok呢,C++认为不管getName如何实现,如果你返回的是一个非const引用,说明你可能回去修改一个可能会消失
(如果是一个临时变量的话)东西;
另外,如果是返回一个const引用,确保了临时变量不会很快消失(不是很懂,不会很快消失,那多久会消失呢)。
C++11中,会让你可以绑定一个mutable右值引用,也就是说,一个值是否临时的,可以使用右值引用来检测,右值引用使用&&语法,可以是const也可以是非const的:
const
string&&
name = getName();
//
ok
string&&
name = getName();
//
also ok - praise be!
有什么用呢?关于左值引用和右值引用,最重要的是,当你写一个函数,函数的参数是左值引用或者右值引用时,比如:
[cpp] view plaincopy
- printReference (constString& str)
- {
- cout << str;
- }
- printReference (String&& str)
- {
- cout << str;
- }
[cpp] view plaincopy
- 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;
- };
- 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构造,那么一定要传一个临时变量,并且一定要是一个可以修改的临时变量