2008-11-11 6 views
7

Jeśli mam następujący kod,Czy myVector.erase (myPtr) usuwa obiekt wskazany przez myPtr?

Foo *f = new Foo(); 
vector<Foo*> vect; 
vect.push_back(f); 
// do stuff 
vect.erase(f); 

Czy tworzę przeciek pamięci? Zgaduję, że tak, ale słowo wymazuje daje wrażenie, że usuwa go.

Pisząc to, zastanawiam się, czy nie jest błędem umieszczenie wskaźnika w wektorze STL. Co myślisz?

Odpowiedz

8

Tak, utworzyłeś przeciek pamięci przez to. std :: vector i inne pojemniki po prostu usuwają wskaźnik, nie zwalniają pamięci wskazywanej przez wskaźnik.

Umieszczenie wskaźnika w standardowym pojemniku bibliotecznym nie jest niczym niezwykłym. Problem polega jednak na tym, że musisz usuwać go podczas usuwania z kontenera. Lepszym, ale proste, sposobem na powyższe, jest w użyciu boost :: shared_ptr:

{ 
    boost::shared_ptr<foo> f(new foo); 

    std::vector< boost::shared_ptr<foo> > v; 
    v.push_back(f); 
    v.erase(v.begin()); 
} /* if the last copy of foo goes out of scope, the memory is automatically freed */ 

Następny C++ Standard (nazywane C++ i C++ 1x 0x powszechnie) obejmie std::shared_ptr. Tam będzie można również użyć funkcji std::unique_ptr<T>, która jest szybsza, ponieważ nie pozwala na kopiowanie. Używanie z kontenerami w C++ 0x jest podobne do biblioteki ptr_container w trybie doładowania.

+0

Należy bardzo uważać, aby użyć instrukcji boost :: shared_ptr, zgodnie z instrukcją, zamiast auto_ptr. Kontenerów STL nie można w ogóle używać z auto_ptr (powód jest nieco długi, aby wyjaśnić w tym komentarzu). – Gorpik

+0

Dobrze, zapamiętam to. Dzięki ! – Barth

1

Z pewnością nie jest błędem skierowanie wskaźnika do standardowego kontenera (błędem jest jednak utworzenie kontenera auto_ptr). Tak, musisz jawnie usunąć, aby zwolnić pamięć wskazywaną przez poszczególne elementy, lub możesz użyć jednego z boostów smart pointers.

2

Alternatywnie istnieje boost :: ptr_vector container.

Wie, że trzyma wskaźniki, które posiada i automatycznie usuwa je.

Jako miły wpływ strony, podczas dostępu do elementów zwraca odwołanie do obiektu, a nie wskaźnik, aby kod wyglądał ładnie.

+0

Wielki referencji, dzięki za to. Tak się składa, że ​​ostatnie kilka dni spędzam na refakturowaniu struktur danych container-of-ptrs, boost :: indirect_iterator było pomocne, ale te pojemniki ptr_xxx byłyby jeszcze lepsze, myślę ... Może będę musiał ponownie dokonać refaktoryzacji;) – Roel

1

wektor usuwa zawarte w nim dane. Ponieważ twój wektor zawiera wskaźniki, usuwa jedynie wskaźniki, a nie dane, które mogą lub nie mogą wskazywać.

To dość ogólna zasada w C++, że pamięć jest zwalniana tam, gdzie została przydzielona. Wektor nie przydzielił żadnych wskaźników, więc nie może go zwolnić.

Prawdopodobnie nie powinieneś przechowywać wskaźników w swoim wektorze w pierwszej kolejności. W wielu przypadkach można byłoby lepiej z mniej więcej tak:

vector<Foo> vect; 
vect.push_back(Foo()); 
// do stuff 
vect.erase(f); 

Oczywiście to zakłada, że ​​Foo jest copyable, a jego konstruktor kopia nie jest zbyt kosztowne, ale pozwala uniknąć wycieków pamięci, a don Trzeba pamiętać, aby usunąć obiekt Foo. Innym podejściem byłoby używanie inteligentnych wskaźników (takich jak Boost's shared_ptr), ale może nie być potrzebna semantyka wskaźnika, w takim przypadku proste rozwiązanie jest najlepsze.

1

Kontenery STL nie uwolnią Twojej pamięci.Najlepszą radą jest używanie inteligentnych wskaźników, wiedząc, że std :: auto_ptr nie zmieści się w pojemnikach. Polecam boost :: shared_ptr, lub jeśli twój producent kompilatorów ma obsługę rozszerzeń TR1 (wiele z nich) możesz użyć std :: tr1 :: shared_ptr.

Należy również pamiętać, że wektor nie zwolni nawet pamięci wewnętrznej zarezerwowanej dla wskaźnika. std :: wektory nigdy nie zmniejszają rozmiaru nawet bez wywołania clear(). Jeśli potrzebujesz zmniejszyć rozmiar wektora, musisz uciekać się do tworzenia innego wektora i zamiany zawartości.

2

Aby wyjaśnić, dlaczego wskaźnik nie zostanie usunięty, należy rozważyć

std::vector<char const*> strings; 
strings.push_back("hello"); 
strings.push_back("world"); 
// .erase should not call delete, pointers are to literals 

std::vector<int*> arrays; 
strings.push_back(new int[10]); 
strings.push_back(new int[20]); 
// .erase should call delete[] instead of delete 

std::vector<unsigned char*> raw; 
strings.push_back(malloc(1000)); 
strings.push_back(malloc(2000)); 
// .erase should call free() instead of delete 

w ogóle, vector<T*>::erase nie może odgadnąć, jak chcesz zbyć T*.

+0

Zastanów się, co? Powiedz nam, co powinniśmy wziąć pod uwagę, nawet jeśli tylko krótko. Nie wydajesz się niczego wymazywać, a żadne z twoich popychań nie ma żadnych odniesień. Po kilku minutach zastanowienia się pomyślałem, że być może twoim zamiarem było, abyśmy zauważyli problem z twoją "char const *", ale nie jestem pewien, jaki masz zamiar. Proszę, nie zostawiaj nas z wieszakiem na klify. -1 Gdybym miał rację co do twojego zamiaru, pomocne byłoby dodanie "Co by to miało wspólnego z takim i takim przypadkiem". – Aaron

+0

@Aaron: Zastanów się, czy byłoby właściwe dla 'std :: vector :: erase (iter)' wywołanie 'delete * iter', biorąc pod uwagę przykłady powyżej. I aby dać odpowiedź: Nie. Pierwszy przykład wywołuje 'delete' na literale łańcuchowym (nie powinien nic robić), w drugim przykładzie' erase' musiałby wywołać 'delete []', aw trzecim przypadku 'usunąć ' musisz wywołać 'free()'. – MSalters

+0

Dobrze. Prawdopodobnie powinienem był złapać problem z drugą próbką (tablice); nie wiem, dlaczego nie. Trzeci (malloc): przyznam, że od dawna używałem malloc do wszystkiego, o czym zapomniałem, że nie powinno się mieszać new/free lub malloc/delete. Trzecią próbą jest zwłaszcza wyjaśnienie co najmniej jednej lub dwóch linii; z powodu ludzi takich jak ja. ;) Usunięcie -1, a jeśli umieścisz swój komentarz w odpowiedzi, nawet sprawię, że będzie +1. – Aaron

Powiązane problemy