std::vector
sera de gérer la mémoire pour vous, comme toujours, mais cette de la mémoire de pointeurs, pas des objets.
Ce que cela signifie, c'est que vos classes seront perdues dans la mémoire une fois que votre vecteur est hors de portée. Par exemple:
#include <vector>
struct base
{
virtual ~base() {}
};
struct derived : base {};
typedef std::vector<base*> container;
void foo()
{
container c;
for (unsigned i = 0; i < 100; ++i)
c.push_back(new derived());
} // leaks here! frees the pointers, doesn't delete them (nor should it)
int main()
{
foo();
}
Ce que vous devez faire est de vous assurer que vous supprimez tous les objets avant que le vecteur est hors de portée:
#include <algorithm>
#include <vector>
struct base
{
virtual ~base() {}
};
struct derived : base {};
typedef std::vector<base*> container;
template <typename T>
void delete_pointed_to(T* const ptr)
{
delete ptr;
}
void foo()
{
container c;
for (unsigned i = 0; i < 100; ++i)
c.push_back(new derived());
// free memory
std::for_each(c.begin(), c.end(), delete_pointed_to<base>);
}
int main()
{
foo();
}
C'est difficile à maintenir, si, parce que nous ne devons pas oublier d'effectuer une action. Plus important encore, si une exception se fait entre la répartition des éléments et la désallocation de la boucle, la désallocation de la boucle ne jamais exécuter et vous êtes coincé avec la fuite de mémoire, de toute façon! Cela s'appelle de l'exception de sécurité et c'est une critique de la raison pourquoi de libération de la mémoire qui doit être fait automatiquement.
Mieux serait si les pointeurs supprimé eux-mêmes. Les thèses sont appelés des pointeurs intelligents, et de la bibliothèque standard fournit std::unique_ptr
et std::shared_ptr
.
std::unique_ptr
représente un cas unique (non partagé, un seul propriétaire) pointeur vers une certaine ressource. Ce devrait être par défaut de votre pointeur intelligent, et dans l'ensemble le remplacement complet de tout pointeur brut utiliser.
auto myresource = /*std::*/make_unique<derived>(); // won't leak, frees itself
std::make_unique
est manquant dans le C++11 est la norme de contrôle, mais vous pouvez faire un vous-même. Pour créer directement un unique_ptr
(pas recommandé en make_unique
si vous le pouvez), faites ceci:
std::unique_ptr<derived> myresource(new derived());
Unique pointeurs ont la sémantique de déplacement; ils ne peuvent pas être copiés:
auto x = myresource; // error, cannot copy
auto y = std::move(myresource); // okay, now myresource is empty
Et c'est tout que nous avons besoin de l'utiliser dans un conteneur:
#include <memory>
#include <vector>
struct base
{
virtual ~base() {}
};
struct derived : base {};
typedef std::vector<std::unique_ptr<base>> container;
void foo()
{
container c;
for (unsigned i = 0; i < 100; ++i)
c.push_back(make_unique<derived>());
} // all automatically freed here
int main()
{
foo();
}
shared_ptr
a de comptage de références sémantique de copie; il permet de multiples propriétaires de partage de l'objet. Il permet de suivre combien de shared_ptr
s existe pour un objet, et lorsque le dernier cesse d'exister (le comptage à zéro), il libère le pointeur. La copie augmente simplement le nombre de références (et en déplaçant les transferts de propriété à un plus faible, presque sans coût). Vous rendre avec des std::make_shared
(ou directement comme indiqué ci-dessus, mais parce qu' shared_ptr
a interne à rendre les allocations, il est généralement plus efficace et techniquement plus exception-sûr à utiliser, make_shared
).
#include <memory>
#include <vector>
struct base
{
virtual ~base() {}
};
struct derived : base {};
typedef std::vector<std::shared_ptr<base>> container;
void foo()
{
container c;
for (unsigned i = 0; i < 100; ++i)
c.push_back(std::make_shared<derived>());
} // all automatically freed here
int main()
{
foo();
}
Rappelez-vous, en général, vous souhaitez utiliser std::unique_ptr
comme un défaut, car il est plus léger. En outre, std::shared_ptr
peut être construit à partir d'un std::unique_ptr
(mais pas vice-versa), il est donc bon de commencer petit.
Sinon, vous pouvez utiliser un conteneur créé pour stocker des pointeurs vers des objets, comme un boost::ptr_container
:
#include <boost/ptr_container/ptr_vector.hpp>
struct base
{
virtual ~base() {}
};
struct derived : base {};
// hold pointers, specially
typedef boost::ptr_vector<base> container;
void foo()
{
container c;
for (int i = 0; i < 100; ++i)
c.push_back(new Derived());
} // all automatically freed here
int main()
{
foo();
}
Alors qu' boost::ptr_vector<T>
avaient évident utiliser en C++03, je ne peux pas parler de la pertinence maintenant, parce que nous pouvons utiliser std::vector<std::unique<T>>
avec probablement peu ou pas comparables, les frais généraux, mais cette affirmation doit être testé.
Peu importe, jamais explicitement gratuit choses dans votre code. Envelopper les choses pour s'assurer de la gestion des ressources est traitée automatiquement. Vous ne devriez pas avoir cru de posséder des pointeurs dans votre code.
Comme un défaut dans un jeu, je serais probablement aller avec std::vector<std::shared_ptr<T>>
. Nous nous attendons à ce partage de toute façon, il est assez rapide jusqu'à ce que le profilage dit le contraire, c'est sûr, et il est facile à utiliser.