L'allocation dynamique n'est requise que lorsque la durée de vie de l'objet doit être différente de la portée dans laquelle il est créé (cela vaut également pour rendre la portée plus petite et plus grande) et vous avez une raison spécifique pour laquelle le stockage par valeur ne le fait pas. travail.
Par exemple:
std::vector<int> *createVector(); // Bad
std::vector<int> createVector(); // Good
auto v = new std::vector<int>(); // Bad
auto result = calculate(/*optional output = */ v);
auto v = std::vector<int>(); // Good
auto result = calculate(/*optional output = */ &v);
A partir de C ++ 11, nous avons std::unique_ptr
gérer la mémoire allouée, qui contient la propriété de la mémoire allouée.std::shared_ptr
a été créé lorsque vous devez partager la propriété. (vous en aurez besoin moins que ce à quoi vous vous attendez dans un bon programme)
La création d'une instance devient vraiment simple:
auto instance = std::make_unique<Class>(/*args*/); // C++14
auto instance = std::make_unique<Class>(new Class(/*args*/)); // C++11
auto instance = std::make_unique<Class[]>(42); // C++14
auto instance = std::make_unique<Class[]>(new Class[](42)); // C++11
C ++ 17 ajoute également std::optional
ce qui peut vous empêcher d'exiger des allocations de mémoire
auto optInstance = std::optional<Class>{};
if (condition)
optInstance = Class{};
Dès que «instance» sort de la portée, la mémoire est nettoyée. Le transfert de propriété est également simple:
auto vector = std::vector<std::unique_ptr<Interface>>{};
auto instance = std::make_unique<Class>();
vector.push_back(std::move(instance)); // std::move -> transfer (most of the time)
Alors, quand avez-vous encore besoin new
? Presque jamais à partir de C ++ 11. La plupart de ce que vous utilisez std::make_unique
jusqu'à ce que vous atteigniez un point où vous atteignez une API qui transfère la propriété via des pointeurs bruts.
auto instance = std::make_unique<Class>();
legacyFunction(instance.release()); // Ownership being transferred
auto instance = std::unique_ptr<Class>{legacyFunction()}; // Ownership being captured in unique_ptr
En C ++ 98/03, vous devez faire une gestion manuelle de la mémoire. Si vous êtes dans ce cas, essayez de mettre à niveau vers une version plus récente de la norme. Si vous êtes coincé:
auto instance = new Class(); // Allocate memory
delete instance; // Deallocate
auto instances = new Class[42](); // Allocate memory
delete[] instances; // Deallocate
Assurez-vous de suivre correctement la propriété pour ne pas avoir de fuites de mémoire! Les sémantiques de déplacement ne fonctionnent pas encore non plus.
Alors, quand avons-nous besoin de malloc en C ++? La seule raison valable serait d'allouer de la mémoire et de l'initialiser ultérieurement via placement new.
auto instanceBlob = std::malloc(sizeof(Class)); // Allocate memory
auto instance = new(instanceBlob)Class{}; // Initialize via constructor
instance.~Class(); // Destroy via destructor
std::free(instanceBlob); // Deallocate the memory
Même si ce qui précède est valide, cela peut également être fait via un nouvel opérateur. std::vector
en est un bon exemple.
Enfin, nous avons encore l'éléphant dans la pièce: C
. Si vous devez travailler avec une bibliothèque C où la mémoire est allouée dans le code C ++ et libérée dans le code C (ou l'inverse), vous êtes obligé d'utiliser malloc / free.
Si vous êtes dans ce cas, oubliez les fonctions virtuelles, les fonctions membres, les classes ... Seules les structures contenant des POD sont autorisées.
Quelques exceptions aux règles:
- Vous écrivez une bibliothèque standard avec des structures de données avancées où malloc est approprié
- Vous devez allouer de grandes quantités de mémoire (dans la copie de mémoire d'un fichier de 10 Go?)
- Vous disposez d'un outillage vous empêchant d'utiliser certaines constructions
- Vous devez enregistrer un type incomplet