Je pensais: ils disent que si vous appelez destructeur manuellement, vous faites quelque chose de mal. Mais est-ce toujours le cas? Y a-t-il des contre-arguments? Situations où il est nécessaire de l'appeler manuellement, où il est difficile / impossible / peu pratique de l'éviter?
Réponses
Trop de publicités?Toutes les réponses décrivent des cas spécifiques, mais il n'y est une réponse d'ordre général:
Vous appelez la dtor explicitement chaque fois que vous devez tout simplement détruire l' objet (en C++ sens) sans relâcher la mémoire de l'objet réside dans.
Ce qui se passe généralement dans toutes les situation où allocation / libération de mémoire est géré indépendamment de l'objet de la construction / destruction. Dans ces cas, la construction se passe via le placement de nouvelles sur un existant partie de la mémoire et de la destruction qui se passe via explicite dtor appel.
Voici le raw exemple:
{
char buffer[sizeof(MyClass)];
MyClass* p = new(buffer)MyClass;
p->dosomething();
p->~MyClass();
MyClass* p = new(buffer)MyClass;
p->dosomething();
p->~MyClass();
}
Un autre exemple notable est la valeur par défaut std::allocator
lorsqu'il est utilisé par std::vector
: les éléments sont construits en vector
cours push_back
, mais la mémoire est allouée dans les morceaux, de sorte qu'il pré-existe à l'élément de construction. Et donc, vector::erase
devez détruire les éléments, mais pas nécessairement, il libère la mémoire (en particulier si de nouvelles push_back doivent arriver bientôt...).
C'est le "mauvais design" dans le strict sens de la programmation orientée objet (vous devez gérer des objets, pas de mémoire: le fait que les objets nécessitent de la mémoire est un "incident"), c'est une "bonne conception" dans "la programmation de bas niveau", ou dans les cas où la mémoire n'est pas prise à partir de la "boutique" par défaut operator new
achète dans.
C'est un mauvais design, si cela se passe de façon aléatoire autour du code, il est bon, si cela se passe localement à des classes spécialement conçus à cette fin.
Appeler le destructeur manuellement est nécessaire si l'objet a été construit à l'aide d'une surcharge forme d' operator new()
, sauf lors de l'utilisation de la "std::nothrow
" surcharges:
T* t0 = new(std::nothrow) T();
delete t0; // OK: std::nothrow overload
void* buffer = malloc(sizeof(T));
T* t1 = new(buffer) T();
t1->~T(); // required: delete t1 would be wrong
free(buffer);
En dehors de la gestion de la mémoire sur un assez faible niveau comme ci-dessus, l'appel des destructeurs explicitement, cependant, est un signe de mauvaise conception. Probablement, il est effectivement pas mal de design, mais carrément mauvais (oui, à l'aide d'un destructeur explicite suivie par un constructeur de copie d'appel à l'opérateur d'affectation est une mauvaise conception et susceptibles d'être mauvais).
Avec C++ 2011, il est une autre raison d'utiliser un destructeur explicite des appels: Lors de l'utilisation généralisée des syndicats, il est nécessaire de détruire explicitement l'objet courant et de créer un nouvel objet à l'aide de placement de nouveau lorsque vous changez le type de l'objet représenté. Aussi, lorsque l'union est détruit, il est nécessaire d'appeler explicitement le destructeur de l'objet courant si elle exige la destruction.
Non, vous ne devez pas appeler explicitement, c'est parce qu'il serait appelé deux fois. Une fois pour le manuel de l'appel et une autre fois lors de l'étendue dans laquelle l'objet est déclaré se termine.
Par exemple.
{
Class c;
c.~Class();
}
Si vous avez vraiment besoin pour effectuer les mêmes opérations, vous devriez avoir une méthode distincte.
Il y a une situation spécifique dans laquelle vous souhaiterez peut-être appeler un destructeur sur un objet alloué dynamiquement avec un placement new
mais le son n'est pas quelque chose que vous aurez jamais besoin.
Comme indiqué dans la FAQ, vous devez appeler le destructeur explicitement lorsque vous utilisez placement new .
C'est à peu près le seul moment où vous appelez explicitement un destructeur.
Je conviens cependant que cela est rarement nécessaire.
Chaque fois que vous avez besoin de séparer l'allocation de l'initialisation, vous avez besoin d'un nouvel emplacement et d'un appel explicite du destructeur manuellement. Aujourd'hui, cela est rarement nécessaire, car nous avons les conteneurs standard, mais si vous devez implémenter un nouveau type de conteneur, vous en aurez besoin.