web-dev-qa-db-fra.com

Nettoyer une liste STL/vecteur de pointeurs

Quel est le plus petit morceau de C++ que vous pouvez créer pour nettoyer en toute sécurité un vecteur ou une liste de pointeurs? (en supposant que vous deviez appeler delete sur les pointeurs?)

list<Foo*> foo_list;

Je préfère ne pas utiliser Boost ou envelopper mes pointeurs avec des pointeurs intelligents. 

49
twk

Puisque nous jetons le gant ici ... "Le plus petit morceau de C++"

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

foo_list . remove_if ( deleteAll );

Je pense que nous pouvons faire confiance aux personnes qui ont mis au point STL pour avoir des algorithmes efficaces. Pourquoi réinventer la roue?

52
Mr.Ree

Pour std::list<T*> utiliser:

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

Pour std::vector<T*> utiliser:

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

Je ne sais pas pourquoi j'ai pris front au lieu de back pour std::list ci-dessus. Je suppose que c'est le sentiment que c'est plus rapide. Mais en réalité, les deux sont du temps constant :). Quoi qu’il en soit, emballez-le dans une fonction et amusez-vous:

template<typename Container>
void delete_them(Container& c) { while(!c.empty()) delete c.back(), c.pop_back(); }
53
for(list<Foo*>::const_iterator it = foo_list.begin(); it != foo_list.end(); ++it)
{
    delete *it;
} 
foo_list.clear();
29
Douglas Leeder

Si vous autorisez C++ 11, vous pouvez créer une version très courte de la réponse de Douglas Leeder:

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

Il est très dangereux de s’appuyer sur du code situé en dehors du conteneur pour supprimer vos pointeurs. Que se passe-t-il lorsque le conteneur est détruit en raison d'une exception levée, par exemple?

Je sais que vous avez dit que vous n'aimiez pas le boost, mais considérez s'il vous plaît le conteneurs de pointeur de boost .

13
Mark Ransom
template< typename T >
struct delete_ptr : public std::unary_function<T,bool>
{
   bool operator()(T*pT) const { delete pT; return true; }
};

std::for_each(foo_list.begin(), foo_list.end(), delete_ptr<Foo>());
9
John Dibling

Je ne suis pas sûr que l'approche de foncteur gagne par souci de brièveté ici.

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

Je déconseillerais généralement cela, cependant. Enveloppant les pointeurs dans des pointeurs intelligents ou en utilisant un conteneur de pointeurs spécialisé, en général, sera plus robuste. Il existe de nombreuses façons de supprimer des éléments d'une liste (diverses variantes de erase, clear, destruction de la liste, affectation via un itérateur dans la liste, etc.). Pouvez-vous garantir de les attraper tous?

6
CB Bailey

Le hack suivant supprime les pointeurs lorsque votre liste sort de son champ d’application avec RAII ou si vous appelez list :: clear ().

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

Exemple:

std::list<Deleter<Foo> > foo_list;
foo_list.Push_back(new Foo());
foo_list.clear();
5
Linoliumz

En fait, je pense que la bibliothèque STD fournit une méthode directe de gestion de la mémoire sous la forme de la classe allocator

Vous pouvez étendre la méthode deallocate () de l'allocateur de base pour supprimer automatiquement les membres de tout conteneur.

Je/pense/c'est le type de chose à laquelle il est destiné.

4
Jeremy Cowles

Au moins pour une liste, itérer et supprimer, puis appeler en clair à la fin est un peu inutile, car il faut parcourir la liste deux fois, alors qu’il ne faut le faire qu’une fois. Voici un peu mieux

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

Cela dit, votre compilateur sera peut-être assez intelligent pour combiner les deux de toute façon, en fonction de la manière dont list :: clear est implémenté.

4
Greg Rogers
for(list<Foo*>::const_iterator it = foo_list.begin(); it != foo_list.end(); it++)
{
    delete *it;
} 
foo_list.clear();

Il y a une petite raison pour laquelle vous ne voudriez pas faire cela: vous parcourez effectivement la liste deux fois.

std :: list <> :: clear est de complexité linéaire; il supprime et détruit un élément à la fois dans une boucle.

Compte tenu de ce qui précède, la solution la plus simple à lire, à mon avis, est la suivante:

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

Depuis C++ 11:

std::vector<Type*> v;
...
std::for_each(v.begin(), v.end(), std::default_delete<Type>());

Ou, si vous écrivez du code basé sur un modèle et que vous souhaitez éviter de spécifier un type concret:

std::for_each(v.begin(), v.end(),
    std::default_delete<std::remove_pointer<decltype(v)::value_type>::type>());

Lequel (depuis C++ 14) peut être raccourci comme:

std::for_each(v.begin(), v.end(),
    std::default_delete<std::remove_pointer_t<decltype(v)::value_type>>());
3
ostappus
void remove(Foo* foo) { delete foo; }
....
for_each( foo_list.begin(), foo_list.end(), remove );
1
kendotwill

Cela semble le plus propre imo, mais votre version c ++ doit supporter ce type d’itération (je pense que tout ce qui inclut c ++ 0x ou avant fonctionnera)

for (Object *i : container) delete i;    
container.clear();
0
ComputerEngineer88
for (list<Foo*>::const_iterator i = foo_list.begin(), e = foo_list.end(); i != e; ++i)
    delete *i;
foo_list.clear();
0
Assaf Lavie