2009-01-31 38 views
2

昨天我看了同事的一些代碼和跨越這來了:擦除類的所有成員

class a_class 
{ 
public: 
    a_class() {...} 
    int some_method(int some_param) {...} 

    int value_1; 
    int value_2; 
    float value_3; 
    std::vector<some_other_class*> even_more_values; 
    /* and so on */ 
} 

a_class a_instances[10]; 

void some_function() 
{ 
    do_stuff(); 
    do_more_stuff(); 

    memset(a_instances, 0, 10 * sizeof(a_class)); // <===== WTF? 
} 

那是合法的(世跆聯線,而不是公共屬性)?對我來說,它聞起來真的很糟糕...... 使用VC8編譯時代碼運行良好,但當調用時,但在訪問任何其他成員時,它會引發VC9編譯時的「意外異常」。任何見解?

編輯:將存儲從memset(&a_instances...更改爲memset(a_instances...。感謝您指出Eduard。
EDIT2:刪除了ctor的返回類型。謝謝你。

結論:謝謝你們,你們證實了我的懷疑。

回答

9

這是初始化C的結構被廣泛接受的方法實施「清除」的方法。
在C++中它不適用,因爲你不能假設任何關於vector的內部結構。清零它很可能會使其處於非法狀態,這就是爲什麼您的程序崩潰。

4

我不確定,但我認爲memset會擦除向量的內部數據。

4

當清零a_instances時,還可以將std_vector清零。構建時可能會分配一個緩衝區。現在,當您嘗試push_back時,它將緩衝區的指針視爲NULL(或其他內部成員),因此會引發異常。

如果你問,這是不合法的。這是因爲你不能通過指針重載書寫,因爲你可以重載賦值操作符。

+0

好吧,你讓我在那裏。當然,在原始代碼中不是&a_instances,而是a_instances。相應地更改了代碼。謝謝。 – EricSchaefer 2009-01-31 16:05:45

+0

好吧,我正在改變我的答案。 – 2009-01-31 16:07:01

3

您不應該在C++對象上執行memset,因爲它不會調用正確的構造函數或析構函數。

具體地,在這種情況下,even_more_values所有a_instances的元件構件析構函數不被調用。

實際上,至少對於你列出的成員(在/ *之前等* /),你不需要調用memset或創建任何特殊的析構函數或clear()函數。所有這些成員都會被默認的析構函數自動刪除。

+0

那麼,你*可以*,但... – dmckee 2009-01-31 15:54:42

+0

固定爲「不應該」:-) – 2009-01-31 15:56:08

+0

::聳聳肩::我明白了。只是在一個慵懶的週六早上感覺相反。 – dmckee 2009-01-31 15:57:17

3

你應該在你的類

void clear() 
    { 
    value1=0; 
    value2=0; 
    value_3=0f; 
    even_more_values.clear(); 
    } 
5

他在非POD類類型上使用memset。這是無效的,因爲C++只允許它用於最簡單的情況:如果一個類沒有用戶聲明的構造函數,析構函數,沒有虛函數和其他幾個限制。它的一系列對象不會改變這個事實。

如果他刪除了矢量,但使用memset就可以了。一個音符雖然。即使它不是C++,它對於他的編譯器可能仍然有效 - 因爲如果標準說某事有未定義的行爲,實現可以做他們想要的一切 - 包括祝福這種行爲並說出會發生什麼。在他的情況下,會發生什麼情況可能是你應用了memset,它會默默地清除向量中的任何成員。可能的指針指向已分配的內存,現在只會包含零,但不知道這一點。

你可以建議他將其清除出使用這樣的事情:

... 
for(size_t i=0; i < 10; i++) 
    objects[i].clear(); 

而且使用像寫清楚:

void clear() { 
    a_object o; 
    o.swap(*this); 
} 

交換實現只想與一個交換鄰矢量*這一點,並清除其他變量。交換矢量特別便宜。他當然需要編寫交換函數,然後交換矢量(even_more_values.swap(that.even_more_values))和其他變量。

3

其中最糟糕的部分是,如果矢量中有任何東西,那麼現在內存丟失了,因爲構造函數沒有被調用。

絕不會覆蓋C++對象。 EVER。如果它是一個派生對象(並且我不知道std :: vector的細節),則此代碼還會覆蓋該對象的vtable,使其崩潰並損壞。

無論誰寫這篇文章都不明白對象是什麼,需要你解釋他們是什麼以及他們如何工作,以便他們在將來不會犯這種錯誤。

2

你在這裏有什麼可能不會崩潰,但它可能不會做你想要的!清零矢量將不會調用每個a_class實例的析構函數。它也將覆蓋a_class.even_more_values的內部數據(因此,如果您的push_back()memset()之後,您可能會遇到訪問衝突)。

我會做出不同的兩件事情:

  1. 使用std :: vector的爲您的存儲無論在a_classsome_function()
  2. 寫出a_class析構函數是由編譯器自動清理正確

如果你這樣做,存儲進行管理你。

例如:

class a_class 
{ 
public: 
    a_class() {...} 
    ~a_class() { /* make sure that even_more_values gets cleaned up properly */ } 

    int some_method(int some_param) {...} 

    int value_1; 
    int value_2; 
    float value_3; 
    std::vector<some_other_class*> even_more_values; 
    /* and so on */ 
} 

void some_function() 
{ 
    std::vector<a_class> a_instances(10); 

    // Pass a_instances into these functions by reference rather than by using 
    // a global. This is re-entrant and more likely to be thread-safe. 
    do_stuff(a_instances); 
    do_more_stuff(a_instances); 

    // a_instances will be cleaned up automatically here. This also allows you some 
    // weak exception safety. 
} 

請記住,如果even_more_values包含指針到其他對象,則需要在a_class析構函數刪除這些對象。如果可能的話,even_more_values應該包含對象本身而不是指向那些對象的指針(這樣你就不必爲a_class編寫析構函數,編譯器爲你提供的那個就足夠了)。