2015-09-01 178 views
0
#include <iostream> 
static int i=0; 
using namespace std; 
class Movable 
{ 
public: 
    Movable():mId(++i){ 
     cout<<"constructing it "<<mId<<endl; 
    }; 
    Movable (const Movable&)=delete; 
    Movable (Movable&)=delete; 
    void operator=(Movable&)=delete; 
    void operator=(const Movable&)=delete; 

    Movable (const Movable&& aObject) 
    { 
     cout<<"Moving it constant "<<mId<<endl; 
//  mId=++i; 
    }; 
    Movable (Movable&&aObject) 
    { 
     cout<<"Moving it "<<mId<<endl; 
    }; 
    Movable &operator=(Movable&&aObject) 
    { 
     cout<<"Moving it assignment "<<mId<<endl; 
     return *this; 

    } 
    Movable &operator=(const Movable&&aObject) 
    { 
     cout<<"Moving it assignment constant "<<mId<<endl; 
     return *this; 

    } 
    ~Movable() 
    { 
     cout<<"destroying it "<<mId<<endl; 
    } 

    int getId() const { 
     return mId; 
    } 

private: 
    int mId; 
}; 

Movable&& CreatenNewMovable() 
{ 
    Movable lM; 
    return std::move(lM); 
} 

int main() { 
    Movable a; 

    a=CreatenNewMovable(); 

    return 0; 
} 

这段代码的输出结果是对象销毁

constructing it 1 
constructing it 2 
destroying it 2 
Moving it assignment 1 
destroying it 1 

我是一个有点困惑怎么可能摧毁临时对象,然后第二移动它。这是一个未定义的行为?我错过了关于移动操作的东西?

回答

2

是的,这个功能

Movable&& CreatenNewMovable() 
{ 
    Movable lM; 
    return std::move(lM); 
} 

坏了。

r值引用仍然是一个引用,在这种情况下,它是对函数终止(从其移出之前)销毁的本地堆栈对象的引用。相反,你应该按价值归还它,它仍然会被移出,如果有复制,那么它将是有效的。

+0

我改变了功能 Movable CreateNewMovable() { \t Movable lM; \t return lM;现在 其构建1 其构建2 移动它分配1 摧毁2 毁坏它1. 这意味着该对象被移动然后破坏 } 输出。 与复制操作有什么不同? –

+1

对于所讨论的类而言,如果移动的执行效率比复制的效率更高,它就不同了。在例如一个std :: vector,移动操作的效率更高,因为它不分配新的内存,并且不会复制或移动所有的成员对象 - 移动通过“盗取”对象拥有的指针来工作从...移开。 –

+0

你可能想看看这里:http://stackoverflow.com/questions/3106110/what-are-move-semantics?rq=1和在这里:http://stackoverflow.com/questions/6943805/are-moved-从对象要求的将要-自毁?RQ = 1 –

2

看看这个片断:

Movable&& CreatenNewMovable() 
{ 
    Movable lM; 
    return std::move(lM); 
} 

其实这是未定义的行为。有两个问题:

  1. 函数只能通过值(可能是参考值)返回它的值。所以,您必须在此替换Movable&&Movable
  2. 在堆栈上创建Movable lM;并将其引用到函数之外是UB。当函数退出时,该对象不再存在。简单地按价值归还 - 在你的情况下,复制elision将生效。

最后,一个有效的办法是:

Movable CreatenNewMovable() 
{ 
    Movable lM; 
    return lM; 
    // ..or.. 
    // Even better to return like this(copy elision has less chances to fail) 
    // return Movable(); 
} 

这将产生导致你在等待:

$ ./w 
constructing it 1 
constructing it 2 
Moving it assignment 1 
destroying it 2 
destroying it 1 

您可以在您的main删除空对象创建减少甚至这项工作:

int main() { 
    Movable a=CreatenNewMovable(); 
    return 0; 
} 
/* Results: 
$ ./w 
constructing it 1 
destroying it 1 
*/