2013-09-21 84 views
1

這裏是我的問題 說我有一個功能,其中包括這樣的事情:析構函數調用和指針

void function() { 
    entity e; //entity is just a class 
    entities.push_back(e); //entities is a vector of entity objects 
} 

這裏的什麼困擾着我。 'e'包含一個指向另一個對象的指針。當實體的析構函數被調用時,它將刪除該指針。既然'e'和實體中的實體指向相同的地方,如果我對實體的指針做了某些操作(在函數之外),它會給出一個錯誤,因爲一旦函數中的指針就會被刪除回。解決這個問題的最好方法是什麼?

回答

5

C++是一種「基於複製」的語言,例如一個容器Entity的確將副本放在容器中。

複製是用C++在許多地方製作的,所以最好是你的課正確支持它們,或者你完全禁止它們。

您的類包含一個指向其他數據的指針:當您複製該類的一個實例時應該發生什麼?如果複製指針是可以的,那麼很明顯,刪除析構函數中的指向對象是不行的,因爲仍然存在的副本將指向已刪除的對象。

有一個簡單的規則,有助於避免這種錯誤,被稱爲「三個規則」。如果你已經明確地編碼要麼

  • 拷貝構造函數
  • 析構函數
  • 賦值運算符
在類

那麼最有可能,你需要他們三個的。

在這種情況下,您的析構函數不是默認的析構函數(因爲刪除了指向的對象),所以您還需要告訴在複製構造或賦值的情況下該怎麼做。

如果你喜歡這個類是不可複製的,然後只是確保

struct Entity { 
    Object *o; 

    Entity(Object *o) : o(o) { 
     ... 
    } 

    ~Entity() { 
     delete o; 
    } 

private: 
    // Taboo... this should just never happen!!! 
    // Here is a declaration, but no implementation will be written 
    Entity(const Entity& other);  // Copy constructor 
    Entity& operator=(const Entity&); // Assignment 
}; 

宣佈禁止運營private將確保用戶代碼永遠不會打電話給他們(這將是一個編譯時錯誤),和只是聲明它們而不提供實現將確保即使類代碼本身也不會錯誤地調用它們(您會得到鏈接時錯誤)。

但是,在這種特定情況下,這會禁止您的代碼將Entity實例放入容器中(容器內的元素必須被複制)。你可以把Entity指針放在一個容器中(指針可以被複制,所以std::vector<Entity *>對於entities將是合法的),但是你將負責處理對象的正確生命週期(誰應該調用析構函數以及何時應該發生? )。

如果在另一方面,你有一個指針類的內部數據,並且要允許使類的一個實例的一個副本,您可以:

  • 副本也有針對性的數據
  • 分享指出不同實例

爲了一個共同的方法是使用「引用計數」指針第二溶液,即尖數據之間的數據「知道」多少指針引用它,並只有在此計數達到0時才銷燬。

+0

謝謝。在我眼中非常具有描述性。 – BWG

+0

同意,非常明確和有用的描述一個重要問題。 – john

+0

在C++ 11中,'= delete'比沒有實現的private更好。 (「3的規則」變成「5的規則」)。有很好的智能指針(unique_ptr/shared_ptr)。 – Jarod42

3

將指針改爲共享對象以包含shared_ptr將是一個簡單的解決方案。

+0

將**每個指針**更改爲共享對象,也許? – qdii

+2

如果不理解背後的原因,這也將是一個可怕的解決方案。 – 6502

+0

嗯。你能舉一個如何做到這一點的例子嗎? – BWG

3

這是因爲你違反了rule of three:如果你有一個析構函數,你幾乎肯定需要一個拷貝構造函數和一個賦值操作符。

當然,處理指針的最好方法是找到一種根本不具有指針的方式(「零規則」):在像編譯器生成的析構函數,構造函數和賦值運算符那樣的情況下會小心爲您自動管理資源。

+0

我將如何去應用這個? – BWG

+0

@ user2159051應用三個規則很簡單,但增加了維護:將一個拷貝構造函數和一個賦值運算符添加到'entity',以便將e中的指針所指向的對象複製並分配一個新的指針到「實體」的副本。應用「零規則」更加困難 - 不是將指向某個對象的指針粘貼到「實體」中,而是放置對象本身。這對於小對象比對大對象更好,使用指針可能是更好的選擇。 – dasblinkenlight

0

如果實體類具有析構函數,則它還應該通過rule of three定義一個複製和複製賦值運算符。

不幸的是,這大概意味着您必須複製對象中指向的項目,以便在銷燬時對象的副本不會刪除稍後將被其他副本刪除的項目。 「不幸」,因爲創建指向項目的副本往往是不受歡迎的;在這種情況下,你有一個設計難題 - 儘管湯姆克爾的建議WRT shared_ptr是一個不錯的選擇。

另一解決方案是通過使用= delete,以及製備entity指針數組代替(和push_back(&e))禁止拷貝,通過使複製構造私有,或(C++ 11)。