2013-08-02 65 views
8

這不是一個關於爲什麼要編寫這樣的代碼的問題,而更多的是關於如何執行一個方法與它綁定的對象有關的問題。如果對象調整其自己的容器,會發生什麼情況?

如果我有一個像結構:

struct F 
{ 
    // some member variables 
    void doSomething(std::vector<F>& vec) 
    { 
     // do some stuff 
     vec.push_back(F()); 
     // do some more stuff 
    } 
} 

我用它是這樣的:

std::vector<F>(10) vec; 
vec[0].doSomething(vec); 

發生什麼情況,如果在doSomething(...)push_back(...)導致矢量擴展?這意味着vec[0]將被複制,然後在執行其方法的過程中被刪除。這不會有好處。

有人可以解釋究竟發生了什麼嗎?

  • 程序是否會立即崩潰?該方法是否試圖對不存在的數據進行操作?
  • 該方法是否在對象的「孤立」中運行,直至遇到像更改對象狀態一樣的問題?

我感興趣的是方法調用與關聯對象的關係。

回答

7

是的,這很糟糕。如果您的對象在doSomething()內部,則可以將對象複製(或者在C++ 11中移動,如果區分與您的代碼相關)。所以在push_back()返回之後,這個指針可能不再指向你的對象的位置。對於vector :: push_back()的特定情況,可能由此指向的內存已被釋放,並將數據複製到其他位置的新數組中。對於其他容器(例如列表),將它們的元素留在原地,這可能(可能)不會導致任何問題。

實際上,您的代碼不太可能立即崩潰。最可能的情況是寫入空閒內存和F對象狀態的無聲損壞。你可以使用像valgrind這樣的工具來檢測這種行爲。

但基本上你有正確的想法:不要這樣做,這是不安全的。

+0

感謝您的答案。您是否認爲該計劃可能會繼續進行而不會崩潰?如果我將數組的邊界寫入空閒內存,則會被檢測到。什麼可以讓一種方法能夠毫無問題地寫入空閒內存? – user487100

+2

您幾乎可以永久寫入「釋放」的內存而不會崩潰。這就是內存映射的工作方式。地圖保持原位以供將來的物體使用。但是,您可以**從不**寫入已釋放的內存「沒有問題」。只是「問題」不是崩潰。 –

+0

@ user487100:如果您將數組的邊界寫入空閒內存,則會檢測到_sometimes_。製作一個案例我可以很容易地寫下來,但是我可以寫出超過最後的結果,而不是一段時間內檢測出來。分配一個'char * a = new char [1]',然後讀寫'a [1]',它可能不會在大多數系統上觸發任何錯誤。 –

3

有人可以解釋究竟發生了什麼嗎?

是的。 如果訪問該對象,經過push_backresizeinsert已經重新分配了vector的內容,這是不確定的行爲,這意味着什麼實際上發生的事情是到你的編譯器,您的操作系統,有什麼do some more stuff是,也許一些其他因素可能是月相,空氣溼度在一些遙遠的位置,......你的名字;-)

總之,這是(間接通過std::vector實施)調用對象本身的析構函數,所以該對象的生命週期已結束。此外,由對象先前佔用的內存已由vector的分配器釋放。因此,使用對象的非靜態成員會導致未定義的行爲,因爲傳遞給函數的指針不會再指向對象。但是,您可以訪問/致電該類的靜態成員:

struct F 
{ 
    static int i; 
    static int foo(); 

    double d; 
    void bar(); 

    // some member variables 
    void doSomething(std::vector<F>& vec) 
    { 
    vec.push_back(F()); 

    int n = foo(); //OK 
    i += n;  //OK 

    std::cout << d << '\n'; //UB - will most likely crash with access violation 
    bar();     //UB - what actually happens depends on the 
          //  implementation of bar 
    } 
} 
相關問題