Pulizia di una lista STL / vettore di puntatori

Qual è il frammento più breve di C ++ che puoi escogitare per pulire in modo sicuro un vettore o un elenco di puntatori? (supponendo che tu debba chiamare delete sui puntatori?)

list foo_list; 

Preferirei non usare Boost o avvolgere i puntatori con puntatori intelligenti.

Visto che stiamo buttando giù il guanto di sfida qui … “Il pezzo più corto di C ++”

 static bool deleteAll( Foo * theElement ) { delete theElement; return true; } foo_list . remove_if ( deleteAll ); 

Penso che possiamo fidarci della gente che è venuta con STL ad avere algoritmi efficienti. Perché reinventare la ruota?

Per std::list utilizzare:

 while(!foo.empty()) delete foo.front(), foo.pop_front(); 

Per std::vector usare:

 while(!bar.empty()) delete bar.back(), bar.pop_back(); 

Non sono sicuro del motivo per cui ho preso in considerazione invece di back per std::list sopra. Immagino sia la sensazione che sia più veloce. Ma in realtà entrambi sono costanti :). In ogni caso avvolgetelo in una funzione e divertiti:

 template void delete_them(Container& c) { while(!c.empty()) delete c.back(), c.pop_back(); } 
 for(list::const_iterator it = foo_list.begin(); it != foo_list.end(); ++it) { delete *it; } foo_list.clear(); 

Se consenti il ​​C ++ 11, puoi fare una versione molto breve della risposta di Douglas Leeder:

 for(auto &it:foo_list) delete it; foo_list.clear(); 

È davvero pericoloso affidarsi al codice al di fuori del contenitore per eliminare i puntatori. Cosa succede quando il container viene distrutto a causa di un’eccezione generata, ad esempio?

So che hai detto che non ti piace aumentare, ma per favore considera i contenitori del puntatore boost .

 template< typename T > struct delete_ptr : public std::unary_function { bool operator()(T*pT) const { delete pT; return true; } }; std::for_each(foo_list.begin(), foo_list.end(), delete_ptr()); 

Non sono sicuro che l’approccio del functor vince per brevità qui.

 for( list::iterator i = foo_list.begin(); i != foo_list.end(); ++i ) delete *i; 

Di solito, di solito, sconsiglio di farlo. Racchiudere i puntatori in puntatori intelligenti o usare un contenitore puntatore specialista è, in generale, più robusto. Ci sono molti modi in cui gli oggetti possono essere rimossi da una lista (vari tipi di erase , clear , distruzione della lista, assegnazione tramite un iteratore alla lista, ecc.). Puoi garantire di prenderli tutti?

Il seguente hack cancella i puntatori quando la tua lista diventa fuori campo usando RAII o se chiami list :: clear ().

 template  class Deleter { public: Deleter(T* pointer) : pointer_(pointer) { } Deleter(const Deleter& deleter) { Deleter* d = const_cast(&deleter); pointer_ = d->pointer_; d->pointer_ = 0; } ~Deleter() { delete pointer_; } T* pointer_; }; 

Esempio:

 std::list > foo_list; foo_list.push_back(new Foo()); foo_list.clear(); 

Almeno per una lista, iterando e cancellando, quindi chiamare clear alla fine è un po ‘inutile poiché coinvolge due volte l’elenco, quando devi farlo solo una volta. Ecco un modo un po ‘migliore:

 for (list::iterator i = foo_list.begin(), e = foo_list.end(); i != e; ) { list::iterator tmp(i++); delete *tmp; foo_list.erase(tmp); } 

Detto questo, il compilatore potrebbe essere abbastanza intelligente da combinare in loop le due comunque, a seconda di come list :: clear è implementato.

 for(list::const_iterator it = foo_list.begin(); it != foo_list.end(); it++) { delete *it; } foo_list.clear(); 

C’è un piccolo motivo per cui non vorresti farlo – stai ripetendo efficacemente l’elenco due volte.

std :: list <> :: clear è lineare nella complessità; rimuove e distrugge un elemento alla volta all’interno di un ciclo.

Prendendo in considerazione quanto sopra, la soluzione più semplice da leggere a mio parere è:

 while(!foo_list.empty()) { delete foo_list.front(); foo_list.pop_front(); } 

In realtà, credo che la libreria STD fornisca un metodo diretto di gestione della memoria sotto forma di class allocatore

È ansible estendere il metodo deallocate () dell’allocazione di base per eliminare automaticamente i membri di qualsiasi contenitore.

Io / penso / questo è il tipo di cosa per cui è destinato.

Dal momento che C ++ 11:

 std::vector v; ... std::for_each(v.begin(), v.end(), std::default_delete()); 

Oppure, se stai scrivendo un codice basato su modelli e vuoi evitare di specificare un tipo concreto:

 std::for_each(v.begin(), v.end(), std::default_delete::type>()); 

Quale (dal C ++ 14) può essere abbreviato come:

 std::for_each(v.begin(), v.end(), std::default_delete>()); 
 void remove(Foo* foo) { delete foo; } .... for_each( foo_list.begin(), foo_list.end(), remove ); 
 for (list::const_iterator i = foo_list.begin(), e = foo_list.end(); i != e; ++i) delete *i; foo_list.clear(); 

Questo sembra più pulito, ma la tua versione c ++ deve supportare questo tipo di iterazione (credo che tutto ciò che include o prima di c ++ 0x funzionerà):

 for (Object *i : container) delete i; container.clear();