2011-07-09 67 views
2

考慮下面的例子:的std ::矢量銷燬和意外的內存泄漏

#include <vector> 
class Foo { 
    std::vector<int*> v; 
public: 
    Foo() { 
     this->v.push_back(new int(23)); 
     this->v.push_back(new int(24)); 
     this->v.push_back(new int(25)); 
    } 

    ~Foo() { 
    } 
}; 

int main() { 
    Foo f; 
    return 0; 
} 

當f超出在main()範圍中,f的析構函數被調用,它應該間接自由f.v.根據this,向量v的每個元素的析構函數現在應該被調用。

但是,當我在valgrind中運行這個程序時,我發現int *沒有被釋放。

$ valgrind --leak-check=full ./a.out 

我在這裏錯過了什麼?

+0

可能重複[是否標準::目錄::刪除每個刪除元素的方法調用析構函數?] (http://stackoverflow.com/questions/4260464/does-stdlistremove-method-call-destructor-of-each-removed-element) – fredoverflow

回答

10

std::vector<T>確實在銷燬時調用了T的析構函數。這裏的Tint *int *的析構函數什麼都不做。 int *本身的存儲空間被釋放,但它們指向的int不是。

考慮:

int main() { 
    int *x = new int(23); 
    return 0; 
} 

這表現出同樣的問題;當x超出範圍時,它的析構函數確實調用,併爲指針是x被釋放的存儲,但由於指針的析構函數是一個無操作時,指向的int不釋放。

更重要的是,vector不知道如何分配int。它們可能由new int分配,但它們也可能指向數組內的元素,這些元素使用new int[200]分配,或者它們可能指向malloc的數據,或者它們可能指向mmap的緩衝區,或者它們可能指向結構元素,或兩個向量可能指向相同的int s ...等。vector是不夠聰明的,以此來預測你想要做的事情,所以它使它們保持獨立(另外,給出vector邏輯來刪除指向元素會破壞非指針元素的向量,如std::vector<int>,因爲你不能deleteint!)

您需要可以使用std::vector<int>,或結合使用智能指針與它,例如std::vector<boost::shared_ptr<int> >。請注意,使用智能指針可能會增加開銷;與C++ 0x你應該能夠使用std::vector<std::unique_ptr<int>>結合std::move避免這種開銷。 Boost也有pointer vectors,可以像預期的那樣釋放指向元素。

+0

是的,這回答了我的問題。 int *指向的內存不會被釋放,儘管指針本身是。謝謝! – rustushki

4

矢量v的每個元素的析構函數,現在應該稱爲

是:存儲在所述向量中的int*對象被銷燬(這是有效地無操作)。容器中的指針指向的對象不會被銷燬。

考慮以下,同樣有效的程序:

{ 
    int x; 
    std::vector<int*> v; 
    v.push_back(&x); 
} // x cannot be delete'd because it isn't dynamically allocated. 

您應該使用智能指針,像std::unique_ptrshared_ptr,這樣你就不必擔心內存管理(不使用std::auto_ptr;它與標準庫容器不兼容,因爲它不是真正可複製的)。如果你不使用智能指針,那麼你需要自己銷燬動態對象;正確地做這件事相當困難。

+0

也許需要使用智能指針。 –

+0

請注意,只有在使用C++ 0x時才能在STL容器中使用'unique_ptr',並且在插入項目時使用'std :: move'。 – bdonlan

2

您載體的每個元素都是int *。當int *銷燬時,語言不會自動調用delete。換句話說,它是被銷燬的指針,而不是指針。

2

由於您使用的是關鍵字new,因此將整數分配到堆而不是堆棧。換句話說,他們正在動態分配。換句話說,你需要清理它之後。

指針類型的「析構函數」是簡單地刪除該指針。它不會觸及位於由指針存儲的內存地址處的數據。請看下面的例子:

int a = 5; 
int* i = &a; 
if (true) 
{ 
    int* j = i; 
} //j goes out of scope, should *i and a be deleted? no. 

所以,你需要做到這一點在析構函數:

std::vector<int*>::iterator iter; 
for (iter = v.begin(); iter != v.end(); iter++) 
{ 
    delete *iter; 
}