Est-il permis de delete this;
si l’instruction delete est la dernière instruction qui sera exécutée sur cette instance de la classe? Bien sûr, je suis sûr que l’object représenté par this
-pointer est new
créé.
Je pense à quelque chose comme ça:
void SomeModule::doStuff() { // in the controller, "this" object of SomeModule is the "current module" // now, if I want to switch over to a new Module, eg: controller->setWorkingModule(new OtherModule()); // since the new "OtherModule" object will take the lead, // I want to get rid of this "SomeModule" object: delete this; }
Puis-je faire ceci?
Le FAQ C ++ Lite a une entrée spécifique pour cela
Je pense que cette citation résume bien
Tant que vous faites attention, un object peut se suicider (supprimer ceci).
Oui, delete this;
a défini des résultats, à condition que (comme vous l’avez noté) vous garantissiez que l’object était alloué dynamicment et, bien entendu, n’essayez jamais d’utiliser l’object après sa destruction. Au fil des ans, de nombreuses questions ont été posées sur ce que la norme dit spécifiquement à propos de la delete this;
norme delete this;
, au lieu de supprimer un autre pointeur. La réponse à cette question est assez courte et simple: elle ne dit rien. Il indique simplement que l’opérande de delete
doit être une expression désignant un pointeur sur un object ou un tableau d’objects. Il contient pas mal de détails sur des choses comme la fonction de désallocation (le cas échéant) à appeler pour libérer la mémoire, mais la section entière sur delete
(§ [expr.delete]) ne mentionne pas delete this;
spécifiquement du tout. La section sur les destructeurs mentionne de delete this
dans un endroit (§ [class.dtor] / 13):
Au sharepoint définition d’un destructeur virtuel (y compris une définition implicite (15.8)), la fonction de désallocation de non-masortingce est déterminée comme si, pour l’expression, elle devait apparaître dans un destructeur non virtuel de la classe du destructeur (voir 8.3.5). ).
Cela tend à soutenir l’idée que la norme envisage de delete this;
pour être valide – si elle était invalide, son type ne serait pas significatif. C’est le seul endroit où la norme mentionne delete this;
du tout, pour autant que je sache.
Quoi qu’il en soit, certains considèrent qu’il s’agit de delete this
type de piratage, et de dire à quiconque entend qu’il faut l’éviter. Un problème fréquemment cité est la difficulté de s’assurer que les objects de la classe ne sont alloués que de manière dynamic. D’autres le considèrent comme un langage parfaitement raisonnable et l’utilisent tout le temps. Personnellement, je suis quelque part dans le milieu: je l’utilise rarement, mais n’hésitez pas à le faire quand cela semble être le bon outil pour le travail.
Edit: [principalement en réponse au commentaire de @Alexandre C]: la première fois que vous faites cela est avec un object qui a une vie presque entièrement propre. Un exemple cité par James Kanze était un système de facturation / suivi sur lequel il travaillait pour une compagnie de téléphone. Fondamentalement, lorsque vous décrochez le téléphone, quelque chose en prend note et crée un object phone_call
. À partir de ce moment, l’object phone_call
gère les détails de l’appel téléphonique (en établissant une connexion lorsque vous appelez, en ajoutant une entrée dans la firebase database pour indiquer le début de l’appel, connectez éventuellement plus de personnes si vous effectuez une conférence téléphonique, etc.) Lorsque vous raccrochez, l’object phone_call
effectue sa comptabilité finale (par exemple, ajoute une entrée dans la firebase database pour indiquer quand vous raccrochez, afin qu’ils puissent calculer la durée de votre appel) et se détruit ensuite. La durée de vie de l’object phone_call
est basée sur le moment où vous décrochez / raccrochez le téléphone – du sharepoint vue du rest du système, il est fondamentalement totalement arbitraire, vous ne pouvez donc pas l’ associer à une scope lexicale du code, ou quelque chose sur cette commande.
Pour quiconque se soucie de la fiabilité de ce type de codage: si vous passez un appel téléphonique depuis, ou à travers presque n’importe quelle partie de l’Europe, il y a de fortes chances qu’il soit traité (au moins en partie) par code cela fait exactement cela.
Si cela vous fait peur, il y a un hack parfaitement légal:
void myclass::delete_me() { std::unique_ptr bye_bye(this); }
Je pense que delete this
C ++ est idiomatique, et je présente seulement cela comme une curiosité.
Il y a un cas où cette construction est réellement utile – vous pouvez supprimer l’object après avoir lancé une exception qui nécessite des données de membre de l’object. L’object rest valide jusqu’à ce que le lancer ait lieu.
void myclass::throw_error() { std::unique_ptr bye_bye(this); throw std::runtime_exception(this->error_msg); }
Note: si vous utilisez un compilateur plus ancien que C ++ 11, vous pouvez utiliser std::auto_ptr
au lieu de std::unique_ptr
, cela fera la même chose.
L’une des raisons pour lesquelles C ++ a été conçu était de faciliter la réutilisation du code. En général, C ++ devrait être écrit de manière à ce que la classe soit instanciée sur le tas, dans un tableau ou sur la stack. “Delete this” est une très mauvaise pratique de codage car elle ne fonctionnera que si une seule instance est définie sur le tas; et mieux vaut ne pas créer une autre instruction de suppression, généralement utilisée par la plupart des développeurs pour nettoyer le tas. Faire cela suppose également qu’aucun programmeur de maintenance à l’avenir ne résoudra une fuite de mémoire faussement perçue en ajoutant une instruction de suppression.
Même si vous savez à l’avance que votre plan actuel est de n’allouer qu’une seule instance sur le tas, que se passe-t-il si des développeurs heureux partent dans le futur et décident de créer une instance sur la stack? Ou, s’il coupe et colle certaines parties de la classe à une nouvelle classe qu’il a l’intention d’utiliser sur la stack? Lorsque le code atteint “delete this”, il s’éteint et le supprime, mais lorsque l’object sort de la scope, il appelle le destructeur. Le destructeur essaiera alors de le supprimer à nouveau, puis vous serez arraché. Par le passé, faire quelque chose de ce genre mettait en péril non seulement le programme, mais le système d’exploitation et l’ordinateur devaient être redémarrés. Dans tous les cas, ceci est fortement déconseillé et devrait presque toujours être évité. Je devrais être désespéré, sérieusement plâtré, ou vraiment détester la compagnie pour laquelle j’ai travaillé pour écrire le code qui a fait cela.
C’est autorisé (ne pas utiliser l’object après cela), mais je n’écrirais pas ce code sur la pratique. Je pense que delete this
devrait apparaître uniquement dans les fonctions appelées release
ou Release
et ressembler à void release() { ref--; if (ref<1) delete this; }
: void release() { ref--; if (ref<1) delete this; }
void release() { ref--; if (ref<1) delete this; }
void release() { ref--; if (ref<1) delete this; }
.
Eh bien, dans le modèle COM (Component Object Model), delete this
construction peut faire partie de la méthode Release
appelée à chaque fois que vous souhaitez libérer l’object recherché:
void IMyInterface::Release() { --instanceCount; if(instanceCount == 0) delete this; }
Vous pouvez le faire. Cependant, vous ne pouvez pas y affecter. Ainsi, la raison pour laquelle vous déclarez ceci: “Je veux changer d’avis” semble très discutable. La meilleure méthode, à mon avis, serait que l’object qui contient la vue remplace cette vue.
Bien sûr, vous utilisez des objects RAII et vous n’avez donc pas besoin d’appeler la suppression du tout … n’est-ce pas?
Ceci est l’idiome de base pour les objects comptés par référence.
Le comptage des références est une forme puissante de récupération de mémoire déterministe: il garantit que les objects gèrent leur durée de vie au lieu de se baser sur des pointeurs «intelligents», etc. pour le faire pour eux. L’object sous-jacent est uniquement accessible via les pointeurs intelligents “Référence”, conçus pour que les pointeurs incrémentent et décrémentent un entier membre (le compte de référence) dans l’object réel.
Lorsque la dernière référence est supprimée de la stack ou est supprimée, le décompte de référence ira à zéro. Le comportement par défaut de votre object sera alors un appel à “supprimer ceci” pour la récupération de mémoire – les bibliothèques que j’écris fournissent un appel virtuel “CountIsZero” protégé dans la classe de base afin de remplacer ce comportement pour des choses comme la mise en cache.
La clé pour rendre ce coffre-fort ne permet pas aux utilisateurs d’accéder au CONSTRUCTOR de l’object en question (le protéger), mais de leur faire appeler un membre statique, le “statique Reference CreateT (…)” de type FACTORY. De cette façon, vous savez avec certitude qu’ils sont toujours construits avec des “nouvelles” ordinaires et qu’aucun pointeur brut n’est toujours disponible, alors “supprimer ceci” ne fera jamais exploser.
C’est une vieille question à laquelle on a répondu, mais @Alexandre a demandé “Pourquoi quelqu’un voudrait-il faire cela?”, Et j’ai pensé que je pourrais donner un exemple d’utilisation que je considère cet après-midi.
Code hérité Utilise des pointeurs nus Obj * obj avec un object de suppression à la fin.
Malheureusement, j’ai parfois besoin, pas souvent, de garder l’object plus longtemps en vie.
J’envisage d’en faire un pointeur intelligent de référence. Mais il y aurait beaucoup de code à changer si je ref_cnt_ptr
utiliser ref_cnt_ptr
partout. Et si vous mélangez nus Obj * et ref_cnt_ptr, vous pouvez supprimer l’object implicitement lorsque le dernier ref_cnt_ptr disparaît, même s’il existe encore Obj *.
Je pense donc à créer un explicit_delete_ref_cnt_ptr. C’est-à-dire un pointeur de référence où la suppression est effectuée uniquement dans une routine de suppression explicite. En l’utilisant à un endroit où le code existant connaît la durée de vie de l’object, ainsi que dans mon nouveau code qui maintient l’object plus longtemps en vie.
L’incrémentation et la décrémentation du nombre de références lorsque explicit_delete_ref_cnt_ptr sont manipulées.
Mais ne pas libérer lorsque le compte de référence est considéré comme nul dans le destructeur explicit_delete_ref_cnt_ptr.
La libération uniquement lorsque le compte de référence est considéré comme nul dans une opération de type suppression explicite. Par exemple dans quelque chose comme:
template class explicit_delete_ref_cnt_ptr { private: T* ptr; int rc; ... public: void delete_if_rc0() { if( this->ptr ) { this->rc--; if( this->rc == 0 ) { delete this->ptr; } this->ptr = 0; } } };
OK, quelque chose comme ça. Il est inhabituel qu’un type de pointeur compté de référence ne supprime pas automatiquement l’object pointé dans le destructeur de ptr d’origine. Mais il semble que cela pourrait rendre le mixage de pointeurs nus et de pointeurs plus sûrs.
Mais jusqu’ici pas besoin de supprimer ceci.
Mais alors il m’est venu à l’esprit: si l’object pointé, la pointe, sait qu’il est compté comme référence, par exemple si le compte est à l’intérieur de l’object (ou dans une autre table), alors la routine delete_if_rc0 pourrait être une méthode du object pointee, pas le pointeur (intelligent).
class Pointee { private: int rc; ... public: void delete_if_rc0() { this->rc--; if( this->rc == 0 ) { delete this; } } } };
En fait, il n’a pas besoin d’être une méthode membre, mais pourrait être une fonction gratuite:
map keepalive_map; template void delete_if_rc0(T*ptr) { void* tptr = (void*)ptr; if( keepalive_map[tptr] == 1 ) { delete ptr; } };
(BTW, je sais que le code n’est pas tout à fait correct – il devient moins lisible si j’ajoute tous les détails, donc je le laisse comme ça.)
Supprimer ceci est légal tant que l’object est en tas. Vous devez exiger que l’object soit uniquement en tas. La seule façon de le faire est de protéger le destructeur – de cette façon, la suppression peut être appelée UNIQUEMENT à partir de la classe, vous aurez donc besoin d’une méthode qui assurera la suppression.