2010-05-20 58 views
5

那麼,我一般對Valgrind和內存泄漏分析器都很陌生。我必須說,當你開始使用它時有點嚇人,因爲你不能停止想知道你之前可能還有多少漏洞未被解決!是valgrind瘋了還是這是一個真正的std映射迭代器內存泄漏?

因爲我不是C++程序員的經驗,所以我想檢查一下是否肯定是內存泄漏還是Valgrind做假陽性?

typedef std::vector<int> Vector; 
typedef std::vector<Vector> VectorVector; 
typedef std::map<std::string, Vector*> MapVector; 
typedef std::pair<std::string, Vector*> PairVector; 
typedef std::map<std::string, Vector*>::iterator IteratorVector; 

VectorVector vv; 
MapVector m1; 
MapVector m2; 

vv.push_back(Vector()); 
m1.insert(PairVector("one", &vv.back())); 

vv.push_back(Vector()); 
m2.insert(PairVector("two", &vv.back())); 

IteratorVector i = m1.find("one"); 
i->second->push_back(10); 
m2.insert(PairVector("one", i->second)); 

m2.clear(); 
m1.clear(); 
vv.clear(); 

這是爲什麼?不應該clear命令調用每個對象和每個向量的析構函數嗎?

現在做一些試驗後我發現的不同解決方案的泄漏:

1)刪除:

i->second->push_back(10); 

2)添加:

delete i->second; 

3)刪除第二

vv.push_back(Vector()); 
m2.insert(PairVector("two", &vv.back())); 

使用解決方案2)使Valgring打印:10分配,11釋放可以嗎?

因爲我不使用新的爲什麼我應該刪除?

謝謝,任何幫助!

+0

不要使用塊引號格式化代碼,請使用101010圖標(或Ctrl + K)。 – 2010-05-20 10:20:41

+0

爲編碼格式編輯。 – Gorpik 2010-05-20 10:24:28

+1

您對typedefs的使用已使代碼難以理解。 – 2010-05-20 10:26:43

回答

1

你不確定的行爲在這裏:

m1.insert(PairVector("one", &vv.back())); 

vv.push_back(Vector()); 

插入無效迭代器並指向載體,這也意味着你存儲在地圖基本上是指向插入後,一些黑洞的指針引用。

使Valgring打印:10分配,11釋放是否行嗎?

這很奇怪,是不是也打印一些關於雙釋放的東西?

對於解決方案,我建議使用不同於vector的容器(例如listdeque,其變異函數使迭代器失效,但不引用)。或者你可以將指針(最好是智能的,但可能是普通的)存儲到向量中的數據,以便實際數據的地址是穩定的。

+0

雖然我不知道如何擺脫調試調用,但我只能看到錯誤,但它的確提到了無效刪除。我將從現在開始使用Valgrind,所以我希望在接下來的日子裏能夠更加舒適一些...... – 2010-05-20 10:52:09

+2

在你引用的語句中,這不是未定義的行爲(尚未定義)。有一個無效的指針懸掛是完全合法的,*只要你不取消引用*。它只在'i-> second-> push_back(10)'這一行中變得未定義,其中向量指針實際上是解除引用的。 – jalf 2010-05-20 11:34:17

0

你在這裏正在做一些危險的事情。您正在保存指向程序執行過程中可能失效的向量的指針。

std::vector<>::push_back()可能會使任何迭代器或對std::vector<>的引用無效(如果它已滿)。由於std::vector<>保證其內容將被連續存儲(所以你可以使用它來代替數組),當它需要更多的內存時,它必須將自己複製到不同的內存塊,並且原始內容變得無效。

這意味着在您的代碼中所有對push_back()的調用(除了第一個調用)都會導致未定義的行爲,因此可能會發生任何事情。

+0

@Gorpik,謝謝你的提示。但告訴我一些事情,我有地圖和向量的原因是,向量我保證我的對象在內存中很好地對齊,並且地圖用於查找具有特定名稱的對象。無意中,這並不打算用於整數,而是大型遊戲對象......如果我仔細觀察停滯的指針,這對你有意義嗎? – 2010-05-20 10:47:49

+0

現在我明白了什麼,矢量可以將我的對象內存移動到不同的地方,以便在增長時執行,並且可以讓我的地圖變得毫無用處。所以我認爲我最好使用一個簡單的數組。 – 2010-05-20 10:55:26

+0

@Alberto Toglia:不是。如果你確定矢量的大小,你可以在構造中指定它,它永遠不會移動。如果你不這樣做,那麼這個陣列將會過度而不是成長,並且你會在覈心上擁有fandango。 – Gorpik 2010-05-20 11:28:52

2

基本上這條線是造成問題:

i->second->push_back(10); 

這是因爲I->第二可能你這麼做的時候變得無效:

vv.push_back(Vector()); 

第二次。

沒有必要呼叫清除。當vv對象超出範圍時,它會正確地銷燬所有對象。同樣所有的地圖都不擁有任何矢量,因此它們的析構函數不會影響它們指向的矢量。因此您不需要使用清除。

如果你想保持相同的整體解決方案爲你的vv對象創建一個向量列表。然後插入到列表中將不會影響已有的成員,並且您的地圖將正常工作。

std::list<Vector> vv; // insertion into this will not invalidate any other members. 
         // Thus any pointers to members you have will not become invalidated. 

就我個人而言,我認爲你已經過了複雜的事情。
我認爲你可以通過大大簡化這個來達到同樣的結果。
如果向量沒有被多個地圖元素引用,那麼只需將該向量放入地圖。

std::map<std::string, std::vector<int> > m1; 

m1["one"].push_back(10); 
m1["two"].push_back(20);