2010-05-20 13 views
5

Cóż, jestem bardzo nowy w Valgrind i profilerach wycieków pamięci w ogóle. I muszę powiedzieć, że jest trochę przerażające, kiedy zaczniesz ich używać, ponieważ nie możesz przestać zastanawiać się, ile wycieków jeszcze nie zostałoś rozwiązanych!Czy valgrind jest szalony, czy jest to prawdziwy wyciek pamięci w iteratorze std map?

Do tego stopnia, że ​​nie jestem doświadczonym programistą w C++, chciałbym sprawdzić, czy to z pewnością wyciek pamięci, czy też Valgrind robi fałszywy alarm?

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(); 

Dlaczego tak jest? Czy polecenie clear nie powinno wywoływać destruktora każdego obiektu i każdego wektora?

Teraz, po wykonaniu kilku testów mogę znaleźć różne rozwiązania wycieku:

1) podczas usuwania

i->second->push_back(10); 

2) Dodawanie:

delete i->second; 

3) Usuwanie drugiej

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

Korzystanie z rozwiązania 2) sprawia, że ​​wydruk Valgring: 10 allocentów, 11 zwolnień Czy to w porządku?

Ponieważ nie używam nowych, dlaczego mam usunąć?

Dzięki za pomoc!

+0

Nie używaj cytatów blokad do formatowania kodu, użyj ikony 101010 (lub Ctrl + K). –

+0

Edytowane w celu uzyskania formatu kodu. – Gorpik

+1

Używanie przez ciebie typedefs uczyniło ten kod niezrozumiałym dla mnie. –

Odpowiedz

1

Masz niezdefiniowane zachowanie tutaj:

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

vv.push_back(Vector()); 

Insert unieważnia iteratory oraz odnośniki kierujące do wektora, co oznacza również wskaźnik został zapisany w mapie jest w zasadzie wskazując jakiś czarną dziurę po wkładki.

sprawia, że ​​wydruk Valgring: 10 allocentów, 11 zwolnień Czy to jest w porządku?

To dziwne, czy nie jest też drukowane coś o podwójnych zwolnieniach?

Dla rozwiązania, proponuję użyć innego pojemnika niż vector (np. list, lub deque, którego funkcje mutacji unieważniają iteratory, ale nie odniesienia). Można też przechowywać wskaźniki (najlepiej inteligentne, ale mogą być zwykłe) do danych w wektorze, aby adres rzeczywistych danych był stabilny.

+0

Mówi coś o nieprawidłowym usunięciu, chociaż nie wiem, jak pozbyć się wywołań debugowania, więc mogłem zobaczyć tylko błędy. Będę używał Valgrind od teraz, więc spodziewam się, że w najbliższych dniach będzie mu wygodniej ... –

+2

To nie jest niezdefiniowane zachowanie (jeszcze) w cytowanych liniach. Jest to całkowicie legalne, gdy kręci się niepoprawny wskaźnik, * tak długo, jak go nie usuniesz *. Jest tylko niezdefiniowane w linii 'i-> second-> push_back (10)', gdzie wskaźnik wektora jest faktycznie dereferenced. – jalf

0

Robisz niebezpieczne rzeczy z wektorami tutaj. Zachowujesz wskaźniki do wektorów, które mogą stać się nieważne podczas wykonywania programu.

std::vector<>::push_back() może unieważnić wszelkie iteratory lub odwołania do std::vector<>, jeśli było już pełne. Ponieważ std::vector<> gwarantuje, że jego zawartość będzie przechowywana w sposób ciągły (aby można było używać go w miejscu tablicy), kiedy potrzebuje więcej pamięci, musi skopiować się do innego bloku pamięci, a oryginał traci ważność.

Oznacza to, że wszystkie wywołania do push_back() w kodzie (z wyjątkiem pierwszego) prowadzą do niezdefiniowanego zachowania, więc wszystko może się tutaj wydarzyć.

+0

@Gorpik, dzięki za wskazówki. Ale powiedz mi coś, Powodem, dla którego mam mapy i wektory jest to, że przy wektorach gwarantuję, że moje obiekty są ładnie wyrównane w pamięci, a mapy są używane do znajdowania obiektów o określonych nazwach. Oczywiście nie było to zamierzone do użycia z int, ale dużymi obiektami do gier ... Czy to ma sens, jeśli uważam na zatrzymane wskaźniki? –

+0

Co minutę rozumiem, wektor może przenosić pamięć moich obiektów do różnych miejsc podczas wykonywania, i co dalej, czyniąc moje mapy bezużytecznymi. Więc myślę, że lepiej będzie użyć do tego prostej tablicy. –

+0

@Alberto Toglia: Niezupełnie. Jeśli wiesz na pewno rozmiar wektora, możesz go określić na budowie i nigdy się nie ruszy. Jeśli tego nie zrobisz, cóż, tablica pójdzie za burtę zamiast rosnąć, a ty będziesz mieć fandango na rdzeniu. – Gorpik

2

Zasadniczo ta linia jest przyczyną problemu:

i->second->push_back(10); 

To dlatego i-> drugi może stać się nieważne kiedy zrobiłeś:

vv.push_back(Vector()); 

po raz drugi.

Nie ma potrzeby, aby dzwonić usunąć. Kiedy obiekt vv wykracza poza zakres, poprawnie zniszczy wszystkie obiekty. Również wszystkie mapy nie posiadają żadnych wektorów, więc ich destruktory nie mają wpływu na wektory, na które wskazują. W związku z tym korzystanie z przejrzysty nie jest wymagane.

Jeśli chcesz zachować to samo ogólne rozwiązanie, utwórz listę wektorów dla twojego obiektu vv. Wtedy wstawienie na listę nie wpłynie na już istniejących członków, a twoje mapy będą działały poprawnie.

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

Osobiście uważam, że komplikujesz rzeczy.
Myślę, że możesz osiągnąć te same wyniki, znacznie upraszczając to.
Jeśli wektory nie są przywoływane przez wiele elementów mapy, wystarczy umieścić wektor na mapie.

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

m1["one"].push_back(10); 
m1["two"].push_back(20); 
Powiązane problemy