2015-10-24 66 views
6

我有一個關於使用unique-ptrs的問題。我得到this answer建議使用移動對象。我定義如下的一類:在C++ 11中定義移動對象是否有意義?

class B { 
    const string objName; 
public: 

    B (B &&) = default; 
    B & operator= (B &&) = default; 
    B (const B &) = delete; 
    B & operator= (const B &) = delete; 

    B(const string & name) : 
      objName(name) { 

    } 

    virtual ~B(); 

    const string name() const { return objName;}; 
} 

,我這個線叫B:

class A { 
A(){} 
void take(B b); 
} 

A a; 
B b("test"); 
cout<<b.name(); 
a.take(std::move(b)); 
cout<<b.name(); 

我的問題:

  1. 即使我已經拖欠了移動構造函數,我不能寫a.take(b),我正在編譯錯誤。 (b)
  2. 在結果中,「測試」結果顯示「複製構造函數」被刪除,但似乎合乎邏輯的選擇是使用移動構造函數,而不需要寫入std :: move打印兩次。爲什麼在調用move之後b對象沒有被移除?如果b對象仍然存在並且它的一個副本已經發送給a.take(move(b)),那麼這意味着我們沒有任何用於右值對象的移動。
  3. 是否使用上述移動對象(刪除複製構造函數和賦值運算符以及默認移動構造函數和移動賦值)是否是一種很好的做法?
+0

嘗試用更長的字符串替換「test」,看看會發生什麼。一般而言,移動對象的值是不確定的;它可能是舊的價值或不是。 –

+3

如果你想從它移動,你需要從'objName'中移除'const'。 –

+0

@AlanStokes刪除const會導致測試將被打印一次。這是否意味着永恆的記憶永遠不會被移動?或者他們會被複制? – Govan

回答

4

是的,有一點。管理資源(可能是物理資源)的對象不能/不應該在對象之間共享,這是想到的第一個例子。

1)你寫錯了。基於這個問題以及您之前的問題,我認爲您需要這些。

class B { 
    string objName; 
public: 
    B (B &&) = default; 
    B & operator= (B &&) = default; 
    B (const B &) = delete; 
    B & operator= (const B &) = delete; 
    B(const std::string & name) : 
      objName(name) {} 
    virtual ~B() {} 
    std::string name() const { return objName;} 
}; 

class A { 
public: 
    std::vector<B> v; 
    void take(B && b) 
    { 
     v.push_back(std::move(b)); 
    } 
}; 

int main() 
{ 
    A a; 
    B b("test"); 

    std::cout << "Before: " << b.name() << std::endl; 
    a.take(std::move(b)); 
    std::cout << "After: " << b.name() << std::endl; 

    std::cout << "A has elements: " << std::endl; 
    for(B &b : a.v) 
     std::cout << " " << b.name() << std::endl; 
} 

2)您正在訪問已移動的值,這沒有任何意義! This answer已經做了很好的解釋,但下面我還包含了std :: move的STL參考文本。

http://en.cppreference.com/w/cpp/utility/move

除另有規定外,有 從被放置在一個有效的,但不確定狀態被移動的所有標準庫對象。即, 只有沒有先決條件的功能(例如運算符 )才能在對象移出後安全地使用。

3)我發現在我的經驗中有兩個合法用途。在這兩種情況下,只能移動的對象都控制着一個物理資源,如果兩個對象共享這個資源就會破壞一切。

+2

請注意,不需要明確刪除副本和賦值運算符。用戶定義的移動語法禁止複製,請參閱現場示例[這裏](http://coliru.stacked-crooked.com/a/d20de6c4f219a22e)。 – vsoftco

+0

@tweej爲什麼我應該使用move-only objet而不是unique-ptr? – Govan

+0

@Govan:實際上,對於一個不能共享的資源,'unique_ptr'是一個建築物磚,而你的移動物體將它包起來呈現一個愉快的界面。如果它不能共享,你甚至會寫一個拷貝構造函數? –

2

約3:當然有。無論如何,我可以考慮很多可以移動但不能被複制的對象的例子。

一個例子是一個Socket類:
1)你想給一個默認的構造函數爲「空」的Socket仍然沒有收到任何IP或端口。
2)你想給一個構造函數來獲得一個IP和端口。基於結構的Socket會嘗試連接,如果連接失敗
3)明顯的一點就是析構函數可能拋出異常 - 它會斷開該對象並釋放該對象可能持有

任何潛在的系統資源是什麼關於移動構造函數與複製構造函數?
比方說,我有一個功能,創建一個套接字並返回它。如果我調用意味着:
的複製構造函數,則新複製的套接字(返回值)將嘗試連接到源套接字所連接的相同IP和端口。這可能根本不可能。
源套接字將被斷開連接並被破壞

試圖複製套接字很可能會造成巨大的混亂。 相反,移動構造函數很好地解決了這個問題:
返回值接收所有底層操作系統資源而不斷開連接或銷燬它們。
源套接字保持空白,並且析構函數沒有任何內容可以斷開連接或銷燬。

看起來套接字最可能只是被移動,而不是被複制。

這可能適用於「Process」類 - 如果我嘗試通過複製返回一個進程,我可能會嘗試再次打開相同的進程,只關閉原來的進程。一團糟!通過移動周圍的過程我不這樣做。我只是將這個過程從功能轉移到功能上。

+0

感謝您的回答。我瞭解移動和複製之間的差異和差異。我想知道的是需要移動右值對象。您可以通過unique_ptr實現套接字。問題是move(unique_ptr (new Socket()))或move(Socket())之間的差異 – Govan