2013-07-25 10 views
8

Używam wartości std::map do odwzorowania wartości ciągu na MyType *. Moja deklaracja mapa wygląda następująco:Właściwy sposób na zniszczenie mapy z wartościami wskaźnika

map<string, MyType *> *my_map = new map<string, MyType>; 

my_map to prywatna zmienna członkiem jednej z moich klas. Mój problem polega na tym, że nie jestem pewien, jak zniszczyć mapę. Podczas usuwania mapy, chciałbym również zadzwonić pod numer delete na wszystkich MyType * zawartych na mapie. Oto mój obecny destructor:

my_map->erase(my_map->begin(), my_map->end()); 
delete my_map; 

Spowoduje to usunięcie wskazówek zawartych w mapie, albo muszę iterację mapie, aby usunąć każdą wskaźnik przed wywołaniem skasować?

+2

Późniejsze - "map" (i większość (jeśli nie wszystkie?) Pojemników w standardowej bibliotece) nie zostało zaprojektowane do usuwania jakiegokolwiek wskaźnika, który zawiera po zniszczeniu. – Nbr44

+0

Ok dzięki. Dokumentacja, którą przeczytałem, nie była zbyt jasna. Odczytano: "To skutecznie zmniejsza rozmiar kontenera o liczbę usuniętych elementów, które są niszczone." – Max

+3

To powszechne nieporozumienie - sami autorzy punktów są rzeczywiście zniszczeni, ale to nie zmienia stanu pamięci, do której prowadzą. . – Nbr44

Odpowiedz

9

Wskaźniki tylko wskazują. Korzystając z surowych wskazówek, musisz wiedzieć, która część aplikacji jest właścicielem zasobów wskazywanych przez wskaźniki. Jeśli są one własnością mapy, konieczne będzie powtórzenie mapy i wywołanie polecenia delete na każdym wskaźniku przed zniszczeniem mapy. Ale jeśli mapa po prostu zawiera wskaźniki do obiektów należących do innych części kodu, nie musisz nic robić.

Bezpieczniejszym rozwiązaniem jest użycie shared_ptr do zarządzania czasem życia obiektu, który zapewni, że obiekt zostanie poprawnie usunięty po zniszczeniu ostatniej shared_ptr. Możesz przechowywać shared_ptrs wewnątrz mapy i jeśli żadne inne instancje shared_ptr nie odwołują się do obiektów na mapie, obiekty zostaną zniszczone, gdy mapa zostanie zniszczona, zgodnie z potrzebami.

2

Jeśli użyjesz smart pointers zamiast surowych wskaźników, wszystko zostanie automatycznie oczyszczone.

// header: 
using MapType = std::map<std::string, std::shared_ptr<MyType>>; 
shared_ptr<MapType> my_map; 

// usage: 
my_map.emplace("foo", std::make_shared<MyType>()); 

// destructor: 
MyClass::~MyClass() 
{ 
    // nothing! 
} 
3

Spowoduje to usunięcie wskazówek zawartych w mapie [...]?

Nie, z uwagi na podany kod, wycieknie każdy członek mapy.

Z reguły dla każdego new musi być pasujący delete. Masz delete dla mapy, ale żadna dla elementów wewnątrz.

Najbardziej poprawnym rozwiązaniem tego problemu jest niestosowanie alokacji dynamicznej. Wystarczy przechowywać MyType s katalogu, jeśli to możliwe:

map<string, MyType>

... i zamiast dynamicznego przydzielania samego map, sklep, który automatycznie:

map<string,MyType> my_map; 

Jeśli automatyczny czas przechowywania nie jest możliwe dla niektórych powód, a następnie użyj inteligentnego wskaźnika dla alokacji dynamicznych. Biorąc pod uwagę, C++ 11 kompilator użyć unique_ptr (lub rzadziej, shared_ptr nawet weak_ptr) dla elementów w map (. Biorąc pod uwagę, C++ 03 kompilator, używać ich odpowiedniki boost)

map<string, unique_ptr<MyType>> my_map; 

Następnie, po zniszczeniu my_map, wszystkie elementy będą delete d.

Baring wszystko to, jeśli jesteś w sytuacji, gdy żadna z powyższych będzie pracować dla Ciebie (chciałbym przez wysoce podejrzany), a następnie trzeba będzie wykonać iterację mapę youself:

struct deleter 
{ 
    template <typename T> operator() (const T& rhs) const 
    { 
    delete rhs.second; 
    } 
}; 

for_each (my_map->begin(), my_map->end(), deleter()); 

W C++ 11, może to być wykonane lambda, coś wzdłuż linii:

for_each (my_map->begin(), my_map->end(), [](auto item) -> void 
{ 
    delete item.second; 
}); 
1

w nowoczesnych C++, tylko ułatwić sobie życie i użyć wskaźników tylko jeśli bezwzględnie wymagane.

Zacząłeś z tym kodem:

map<string, MyType *> *my_map = new map<string, MyType>; 

Pierwszą rzeczą, jaką możesz zrobić, to należy rozważyć użycie instancji std::map jako członek danych, zamiast wskaźnik do niego.

Następnie, jeśli MyType nie jest super-drogie, aby skopiować i jej przypadki są własnością tylko przez mapę, wystarczy rozważyć prosty map z string do MyType (zamiast MyType*):

// my_map data member - no pointers --> automatically deleted in class destructor 
map<string, MyType> my_map; 

Jeśli naprawdę potrzebujesz mapy zawierającej wskaźniki, rozważ użycie inteligentnych wskaźników , takich jak std::shared_ptr (dostępnych w C++ 11/14) dla współwłasności lub std::unique_ptr dla unikalnego niepodzielonego prawa własności.
(Jeśli cel C++ 98/03 opcją jest użycie boost::shared_ptr Ponieważ nie ma semantyka przenieść, nie można mieć unique_ptr, która jest w dużym stopniu oparty na ruchu semantyka funkcji.).
np

// Map containing _smart_ pointers 
//  --> default destructor is fine (no need for custom delete code) 
map<string, shared_ptr<MyType>> my_map; 

Jak widać, przy użyciu wartość semantykę (zamiast surowych wskaźników) lub inteligentne kursory można uprościć swój kod i użyć automatyczne zniszczenie dostarczonych przez C++.