2011-04-16 46 views
5

這是一流的設計標準的C++ 0x防止副本分配,以保護客戶端代碼免受意外data雙缺失呢?把不可複製的對象爲STD-容器

struct DataHolder { 
    int *data; // dangerous resource 
    DataHolder(const char* fn); // load from file or so 
    DataHolder(const char* fn, size_t len); // *from answers: added* 
    ~DataHolder() { delete[] data; } 

    // prevent copy, to prevent double-deletion 
    DataHolder(const DataHolder&) = delete; 
    DataHolder& operator=(const DataHolder&) = delete; 

    // enable stealing 
    DataHolder(DataHolder &&other) { 
    data=other.data; other.data=nullptr; 
    } 
    DataHolder& operator=(DataHolder &&other) { 
    if(&other!=this) { data = other.data; other.data=nullptr}; 
    return *this; 
    } 
}; 

您會注意到,我在這裏和移動指派方法來定義新的舉措。我是否正確實施它們?

有什麼辦法,我可以 - 與移動移動指派定義 - 把DataHolder在一個標準集裝箱?像vector?我該怎麼做?

我不知道,有些選項進入腦海:

// init-list. do they copy? or do they move? 
// *from answers: compile-error, init-list is const, can nor move from there* 
vector<DataHolder> abc { DataHolder("a"), DataHolder("b"), DataHolder("c") }; 

// pushing temp-objects. 
vector<DataHolder> xyz; 
xyz.push_back(DataHolder("x")); 
// *from answers: emplace uses perfect argument forwarding* 
xyz.emplace_back("z", 1); 

// pushing a regular object, probably copies, right? 
DataHolder y("y"); 
xyz.push_back(y); // *from anwers: this copies, thus compile error.* 

// pushing a regular object, explicit stealing? 
xyz.push_back(move(y)); 

// or is this what emplace is for? 
xyz.emplace_back(y); // *from answers: works, but nonsense here* 

emplace_back想法只是一個猜測,在這裏。

編輯:爲了讀者的方便,我將答案寫入了示例代碼。

+0

const RValue引用? – 2011-04-16 15:29:38

回答

6

您的示例代碼看起來主要是正確。

  1. 常量DataHolder &&other(在兩個地方)。

  2. if(&other!=this)在您的移動賦值運算符看起來不必要,但無害。

  3. 初始化器列表向量構造函數不起作用。這會嘗試複製你的DataHolder,你應該得到一個編譯時錯誤。

  4. 使用rvalue參數的push_back和emplace_back調用將起作用。那些左值參數(使用y)會給你編譯時錯誤。

push_back和emplace_back在使用方式上確實沒有區別。 emplace_back用於當你不想在vector之外構造一個DataHolder,而是傳遞參數來構造一個只在vector中。例如。:

// Imagine this new constructor: 
DataHolder(const char* fn, size_t len); 

xyz.emplace_back("data", 4); // ok 
xyz.push_back("data", 4); // compile time error 

更新:

我只注意到你的舉動賦值運算符中有內存泄漏。

DataHolder& operator=(DataHolder &&other) 
{ 
    if(&other!=this) 
    { 
     delete[] data; // insert this 
     data = other.data; 
     other.data=nullptr; 
    } 
    return *this; 
} 
+1

我不認爲阻止移動自己是無用的。畢竟,'a = std :: move(a)'是有效的,應該是無操作的。 – ltjax 2011-04-16 15:57:32

+0

(1)複製粘貼錯誤。我編輯了出來。 (2)真的嗎?是不是通常的複製模式?臨時對象不能是'this'?嗯。 (3)對,init-list成員的行爲是'const'。好。 (4)啊,那是「emplace」......當然。現在我明白了。 (*)美麗的所有新事物一起工作:右值參考,完美轉發,模板可變參數。大! – towi 2011-04-16 17:00:17

+3

@towi - 通常的複製模式是「複製 - 交換」成語:'T&operator =(T rhs/* pass by value!* /){this-> swap(rhs);返回*這個; }'這有三個好處:1)代表複製ctor,所以不需要複製代碼; 2)如果交換沒有拋出(它不應該!),它是異常安全的; 3)自我分配工作沒有明確的檢查。 'this-> swap(rhs)'(帶'T && rhs')通常也是實現移動賦值操作符的好方法。 – JohannesD 2011-04-16 17:58:33

1

不包含名稱的臨時對象,例如。 DataHolder("a"),在可用時移動。 C++ 0x中的標準容器將始終在可能的情況下移動,這也允許將std::unique_ptr放入標準容器中。
除此之外,你實現你的移動操作錯誤:

// enable stealing 
    DataHolder(const DataHolder &&other) { 
    data=other.data; other.data=nullptr; 
    } 
    DataHolder& operator=(const DataHolder&&other) { 
    if(&other!=this) { data = other.data; other.data=nullptr}; 
    return *this; 
    } 

如何從一個常量對象移動?您不能只更改otherdata,因爲other是恆定的。將其更改爲簡單DataHolder&&

+0

ups,使用'const &&''複製粘貼錯誤。我刪除了常量,以便讀者不會學習這個錯誤的代碼。 – towi 2011-04-16 16:55:55

相關問題