2015-12-02 70 views
1

作爲學術練習,我創建了一個自定義向量實現,我希望支持複製非pod類型。如何最好地處理未初始化內存的可交換成語

我希望容器支持不提供默認構造函數的存儲元素。

當我爲向量保留內存,然後push_back一個元素(它管理它自己的資源並且實現了複製和賦值操作符 - 我忽略了當前的構造函數)我有一個使用copy-交換該類型的成語。

由於交換髮生在仍爲未初始化內存的類型上,所以在交換之後,被臨時調用的析構函數將嘗試釋放某些未初始化的數據,這當然會激化。

我可以看到幾種可能的解決方案。一種是確保所有非pod類型都實現默認構造函數,並在集合中的每個元素上調用該元素(放置新元素)。我不是這個想法的粉絲,因爲它看起來既浪費又麻煩。

另一種方法是在執行交換之前將容器中類型空間的內存mem設置爲0(這樣臨時值將爲空,調用析構函數將無誤地運行)。這對我來說有點不好意思,我不確定是否有更好的選擇(請參閱下面的代碼以獲得一個示例)。您也可以在爲一堆元素調用reserve後將所有保留空間memset設置爲0,但這可能是浪費。

是否有關於如何實現std :: vector的文檔,因爲調用reserve不會調用分配元素的構造函數,而resize會調用(並且對於未實現默認構造函數的構造函數,構造的臨時變量可以作爲第二個參數調用)

下面是一些代碼可以運行來演示問題,我省略了實際的向量代碼,但原理保持不變。

#include <iostream> 
#include <cstring> 

// Dumb example type - not something to ever use 
class CustomType { 
public: 
    CustomType(const char* info) { 
     size_t len = strlen(info) + 1; 
     info_ = new char[len]; 
     for (int i = 0; i < len; ++i) { 
      info_[i] = info[i]; 
     } 
    } 

    CustomType(const CustomType& customType) { 
     size_t len = strlen(customType.info_) + 1; 
     info_ = new char[len]; 
     for (int i = 0; i < len; ++i) { 
      info_[i] = customType.info_[i]; 
     } 
    } 

    CustomType& operator=(CustomType customType) { 
     swap(*this, customType); 
     return *this; 
    } 

    void swap(CustomType& lhs, CustomType& rhs) { 
     std::swap(lhs.info_, rhs.info_); 
    } 

    ~CustomType() { 
     delete[] info_; 
    } 

    char* info_; 
}; 

int main() { 
    CustomType customTypeToCopy("Test"); 

    // Mimics one element in the array - uninitialised memory 
    char* mem = (char*)malloc(sizeof(CustomType)); 

    // Cast to correct type (would be T for array element) 
    CustomType* customType = (CustomType*)mem; 
    // If memory is cleared, delete[] of null has no effect - all good 
    memset(mem, 0, sizeof(CustomType)); 
    // If the above line is commented out, you get malloc error - pointer 
    // being freed, was not allocated 

    // Invokes assignment operator and copy/swap idiom 
    *customType = customTypeToCopy; 

    printf("%s\n", customType->info_); 
    printf("%s\n", customTypeToCopy.info_); 

    return 0; 
} 

任何信息/建議將不勝感激!

解決!

感謝@Brian和@Nim幫助我理解賦值(複製/交換)有效時的用例。

爲了達到我想要的東西,我只是需要更換線路

*customType = customTypeToCopy; 

new (customType) CustomType(customTypeToCopy); 

調用拷貝構造函數沒有賦值操作符!

謝謝!

+0

我已經讀了幾次這個問題(可能是遲到了),但不清楚你是否有執行交換的自定義類型*或矢量*的問題? (順便說一句,我不認爲'memset()'會起作用)並且不清楚在單元化內存中使用copy-swap的原因(意味着你試圖訪問一個超出vector的「bounds」範圍的元素...) – Nim

+0

嘿Nim!對不起,如果我的解釋不是很好,我會盡力澄清。基本上,在交換時,位於未初始化內存值的info_指針會被移入臨時進入該函數的臨時位置,然後當作用域結束時,該臨時對象會調用析構函數,但info_指針只是垃圾,所以調用delete []會導致崩潰。如果我以前memset內存,它將爲空,並且調用delete不起作用。我只想知道這是一件好事還是壞習慣。我想不出一個好的選擇:( – Tom

+0

...但這意味着你*交換*的元素是*出界*(即超出矢量的*大小*) – Nim

回答

1

您不使用複製和交換進行施工。

您可以使用copy-and-swap進行賦值,以解決以下問題:賦值的左側是一個已經初始化的對象,因此需要在複製右側的狀態之前釋放其佔用的資源或進入它;但如果通過拋出異常複製或移動構造失敗,我們希望保持原始狀態。

如果您正在進行構建而不是分配---因爲目標未初始化---通過複製和交換解決的問題不存在。您只需使用新的位置調用構造函數。如果成功,那很好。如果拋出異常失敗,語言保證已經構建的任何子對象被銷燬,並且你只是讓異常向上傳播;在失敗情況下,目標的狀態將與以前相同:未初始化。

+0

啊好吧我看到了 - 所以我的問題就變成了如果該對象沒有默認構造函數?我認爲我需要在該元素上調用新的位置,但是如果我想調用特定的構造函數。有沒有辦法做到這一點?當你調用reserve和push_back元素時,std :: vector如何處理這種情況?非常感謝您的回答! – Tom

+0

@Tom'vector :: push_back'使用複製或移動構造函數。如果這個類型不能被複制或移動,你不能擁有它的一個向量(或者至少你不能'push_back')。 – Brian

+0

好吧,我明白了,謝謝Brian!我有我的解決方案! :)如果我理解正確,我只需要更新代碼,而不是做分配,我這樣做 - 新(customType)CustomType(customTypeToCopy);非常感謝你!! :) – Tom

相關問題