2015-09-01 230 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 
*/