2013-07-25 13 views
8

Sto utilizzando std::map per mappare i valori di stringa su MyType *. La mia dichiarazione di mappa si presenta così:Modo corretto per distruggere una mappa con valori di puntatore

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

my_map è una variabile membro privata di uno dei miei corsi. Il mio problema è che non sono sicuro di come distruggere la mappa. Quando elimini la mappa, mi piacerebbe anche chiamare delete su tutti gli MyType * contenuti nella mappa. Qui è la mia attuale distruttore:

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

Sarà questo eliminare i puntatori contenuti nella mappa, o devo per scorrere la mappa di eliminare ogni puntatore prima di chiamare cancellare?

+2

Il successivo - 'map' (e la maggior parte (se non tutti?) Dei contenitori nella libreria standard) non è stato progettato per eliminare qualsiasi puntatore che contiene in caso di distruzione. – Nbr44

+0

Ok, grazie. La documentazione che ho letto su di esso non era molto chiara. Diceva: 'Questo riduce efficacemente le dimensioni del contenitore per il numero di elementi rimossi, che vengono distrutti. – Max

+3

Questo è un malinteso comune: gli _pointers_ sono effettivamente distrutti, ma ciò non cambia lo stato della posizione di memoria a cui puntano . – Nbr44

risposta

9

I puntatori si limitano a puntare. Quando utilizzi i puntatori grezzi, devi sapere quale parte della tua app possiede le risorse a cui puntano i puntatori. Se sono di proprietà della mappa, dovrai eseguire un'iterazione sulla mappa e chiamare l'eliminazione su ciascun puntatore prima che la mappa venga distrutta. Ma se la mappa contiene solo puntatori agli oggetti che appartengono ad altre parti del tuo codice, non devi fare nulla.

Una soluzione più sicura è utilizzare shared_ptr per gestire la durata dell'oggetto, che garantirà che l'oggetto venga eliminato correttamente quando l'ultimo shared_ptr viene distrutto. È possibile memorizzare shared_ptrs all'interno della mappa e se nessun'altra istanza shared_ptr fa riferimento agli oggetti all'interno della mappa, gli oggetti verranno distrutti quando la mappa viene distrutta, come desiderato.

2

Se si utilizza smart pointers anziché i puntatori grezzi, tutto verrà pulito automaticamente.

// 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

sarà questo eliminare i puntatori contenuti nella mappa [...]?

No, dato il codice che hai fornito, perderai tutti i membri della mappa.

Di norma, per ogni new è necessario corrispondere a delete. Hai un delete per la mappa, ma nessuno per gli elementi all'interno.

La soluzione più corretta a questo problema è di non utilizzare affatto l'allocazione dinamica. Basta memorizzare directory MyType s, se possibile:

map<string, MyType>

... e invece di allocare dinamicamente la map sé, negozio che automaticamente:

map<string,MyType> my_map; 

Se la durata di memorizzazione automatica non è possibile per qualche motivo, quindi utilizzare un puntatore intelligente per le allocazioni dinamiche. Dato un C++ 11 compilatore, usare unique_ptr (o, raramente, shared_ptr o anche weak_ptr) per gli elementi nel map: (. Dato un compilatore C++ 03, utilizzare gli equivalenti Boost loro)

map<string, unique_ptr<MyType>> my_map; 

Quindi, quando my_map viene distrutto, tutti gli elementi saranno delete d.

Baring tutto questo, se si è in una situazione in cui nessuna delle precedenti funziona per voi (lo farei da personale altamente sospetta), allora si avrà bisogno di iterare la mappa youself:

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

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

In C++ 11, questo potrebbe essere fatto un lambda, qualcosa lungo la linea del:

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

Nella moderna C++, basta fare la vita più facile e sull'uso di puntatori solo se strettamente necessario.

Hai iniziato con questo codice:

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

La prima cosa che puoi fare è quello di considerare l'utilizzo di un'istanza std::map come membro di dati, al posto di un puntatore ad esso.

Poi, se MyType non è super-costoso da copiare e le sue istanze sono di proprietà solo dalla mappa, basta considerare una semplice map da string a MyType (invece di MyType*):

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

Se davvero bisogno di una mappa contenente puntatori, considerare l'utilizzo di puntatori intelligenti, come std::shared_ptr (disponibile in C++ 11/14) per la proprietà condivisa, o std::unique_ptr per proprietà non condivise esclusive.
(Se il target di C++ 98/03, la possibilità è quella di utilizzare boost::shared_ptr dato che non c'è la semantica si muovono, non si può avere unique_ptr, che è fortemente basato sul movimento la semantica dispongono..)
esempio:

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

Come si può vedere, utilizzando semantica di valore (invece di puntatori prime), o puntatori intelligenti, è possibile semplificare il codice e utilizzare il distruzione automatica fornito da C++.

Problemi correlati