右值引用
The rule of three
If you need to explicitly declare either the destructor, copy constructor or copy assignment operator yourself, you probably need to explicitly declare all three of them.
Copy and swap惯用法
什么是Copy and swap惯用法
MyArray& operator=(MyArray rhs)
{
std::swap(data, rhs.data);
std::swap(nsize, rhs.nsize);
return *this;
}
MyArray& operator=(const MyArray& rhs)
{
if (this != &rhs){
if (nsize){
delete[] data;
nsize = 0;
data = NULL;
}
if (rhs.nsize){
data = new T[rhs.nsize]();
std::copy(rhs.data, rhs.data + rhs.nsize, data);
nsize = rhs.nsize;
}
}
return *this;
}
上面是copy and swap惯用法和普通方法的代码,它们最终的效果完全一样
为什么copy and swap惯用法正确
注意到copy and swap惯用法中函数签名,MyArray rhs
不是引用。rhs是一个局部变量,这样转移资源的时候就不会出错。而对象本来的资源转移给rhs,由系统释放,而不用我们自己释放。而const MyArray& rhs
是一个引用,不能转移资源。
为什么不用检查是否对自己赋值
在copy and swap惯用法中,a = a
语句也可以正常执行。但是有可能会产生额外的copy代价。
copy and swap惯用法的优点
- copy and swap主要优点是可以显著减少代码量。
- 在C++11中,结合move语义,可以减少不必要的copy操作。
- Exception safety
Move semantic
什么是左值和右值
lvalue 是指那些单一表达式结束之后依然存在的持久对象。例如: obj,*ptr, prt[index], ++x 都是 lvalue。
rvalue 是指那些表达式结束时(在分号处)就不复存在了的临时对象。例如:1729 , x + y , std::string(“meow”) , 和 x++ 都是 rvalue。
右值引用
右值引用解决的是各种情形下对象的资源所有权转移的问题。这里主要讨论非常量右值引用,因为const 右值引用因为无法改变右值。
右值引用的变量在当前语句结束后就会释放,因此后面的代码不会再用到这个右值。所以对它的改变都是安全的。
move语义
MyArray& operator=(MyArray&& rhs)
{
std::swap(data, rhs.data);
std::swap(nsize, rhs.nsize);
return *this;
}
和copy and swap的代码非常相似。但是rhs是个右值引用,因此可以安全的交换数据,函数结束后右值rhs就会被自动释放。
和copy and swap不同的是,move语义会带来效率上的切实提高。因为copy and swap惯用法中rhs是函数创建的临时对象。而move 语义中的rhs则是一个引用,而不是临时创建的对象。因此move语义比常用的operator=
效率会提高。
完美转发
template<typename T>
void f(T t)
{
g(t);
}
我们希望f能够完美转发参数,就是说不管g的参数如何,要求保证const和non-const,左值和右值的性质不变。
首先g的参数如果是非引用的话就不会出现这个问题。
当g的参数带有引用时:
f为
void f(T&)
, 可以正确转发- 左值:const X&, X&
- 右值:const X&
但是对于non-const 的右值,推导为X&,却不能被绑定。(右值只能被绑定为const)
f为
void f(const T&)
, 可以正确转发- 左值:const X&
- 右值:const X&, X&
但是对于non-const左值,f不能转发。
T&&的模板参数推导原则
- 如果实参是左值,那么T就被推导为U&(其中U为实参的类型),于是T&& = U& &&,而U& &&则退化为U&(理解为:左值引用的右值引用仍然是左值引用)。
- 如果实参是右值,那么T就被推导为U,于是T&& = U&&(右值引用)。
完美转发解决方案
template<typename T>
void f(T&& t)
{
g(static_cast<T&&>(t));
}
当参数是左值时g(static_cast<T&&>(t))
是g(static_cast<U&> t)
当参数是右值时g(static_cast<T&&>(t))
是g(static_cast<U&&>(t));
,依然是个不具名的右值引用。
Note
C++虽然不允许定义长度为0的数组变量,但明确指出,调用new创建长度为0的数组是合法的。new返回有效的非零指针。该指针不能与new返回的其他指针不同,不能进行解引用操作,因它没指向任何元素。允许的操作:比较运算。
link
http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
http://stackoverflow.com/questions/3106110/what-is-move-semantics
http://stackoverflow.com/questions/4172722/what-is-the-rule-of-three
http://blog.csdn.net/pongba/article/details/1697636