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.
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?
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(); }
for(list<Foo*>::const_iterator it = foo_list.begin(); it != foo_list.end(); ++it)
{
delete *it;
}
foo_list.clear();
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();
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 .
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>());
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?
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();
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é.
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é.
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();
}
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>>());
void remove(Foo* foo) { delete foo; }
....
for_each( foo_list.begin(), foo_list.end(), remove );
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();
for (list<Foo*>::const_iterator i = foo_list.begin(), e = foo_list.end(); i != e; ++i)
delete *i;
foo_list.clear();