Est-il autorisé à 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'objet représenté par le this
pointeur est new
créé de manière ly.
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?
c++
memory-management
new-operator
delete-operator
self-destruction
Martijn Courteaux
la source
la source
delete this
avez créé un couplage étroit entre la classe et la méthode d'allocation utilisée pour créer des objets de cette classe. C'est une conception OO très médiocre, car la chose la plus fondamentale dans la POO est de créer des classes autonomes qui ne savent pas ce que leur appelant fait ou se soucient de lui. Ainsi, une classe correctement conçue ne devrait pas savoir ou se soucier de la façon dont elle a été allouée. Si, pour une raison quelconque, vous avez besoin d'un mécanisme aussi particulier, je pense qu'une meilleure conception serait d'utiliser une classe wrapper autour de la classe réelle et de laisser le wrapper gérer l'allocation.setWorkingModule
?Réponses:
La FAQ C ++ Lite a une entrée spécialement pour cela
Je pense que cette citation résume bien
la source
Oui,
delete this;
a des résultats définis, tant que (comme vous l'avez noté) vous vous assurez que l'objet a été alloué dynamiquement, et (bien sûr) ne tentez jamais d'utiliser l'objet après sa destruction. Au fil des ans, de nombreuses questions ont été posées sur le contenu de la normedelete this;
, par opposition à la suppression d'un autre pointeur. La réponse est assez courte et simple: elle ne dit pas grand-chose. Il dit simplement quedelete
l'opérande de doit être une expression qui désigne un pointeur sur un objet ou un tableau d'objets. Il entre dans un peu de détails sur des choses comme la façon dont il détermine quelle fonction de désallocation (le cas échéant) appeler pour libérer la mémoire, mais la section entière surdelete
(§ [expr.delete]) ne mentionne pas du toutdelete this;
spécifiquement. La section sur les destructeurs mentionnedelete this
en un seul endroit (§ [class.dtor] / 13):Cela tend à soutenir l'idée que la norme considère
delete this;
comme valide - si elle n'était pas valide, son type ne serait pas significatif. C'est le seul endroit que la norme mentionnedelete this;
, à ma connaissance.Quoi qu'il en soit, certains considèrent
delete this
un hack méchant et disent à quiconque écoutera qu'il devrait être évité. Un problème fréquemment cité est la difficulté de garantir que les objets de la classe ne sont jamais alloués dynamiquement. D'autres le considèrent comme un idiome parfaitement raisonnable et l'utilisent tout le temps. Personnellement, je suis quelque part au milieu: je l'utilise rarement, mais n'hésitez pas à le faire quand il semble être le bon outil pour le travail.La première fois que vous utilisez cette technique, c'est avec un objet qui a une vie qui lui est presque entièrement propre. Un exemple que James Kanze a cité est un système de facturation / suivi sur lequel il a travaillé pour une compagnie de téléphone. Lorsque vous commencez à téléphoner, quelque chose en prend note et crée un
phone_call
objet. À partir de ce moment, l'phone_call
objet gère les détails de l'appel téléphonique (établir une connexion lorsque vous composez, ajouter une entrée à la base de données pour dire quand l'appel a commencé, connecter éventuellement plus de personnes si vous effectuez une conférence téléphonique, etc.) les dernières personnes ayant raccroché, l'phone_call
objet fait sa comptabilité finale (par exemple, ajoute une entrée à la base de données pour dire quand vous avez raccroché, afin qu'ils puissent calculer la durée de votre appel), puis se détruit. La durée de vie duphone_call
l'objet est basé sur le moment où la première personne commence l'appel et lorsque les dernières personnes le quittent - du point de vue du reste du système, il est fondamentalement entièrement arbitraire, vous ne pouvez donc le lier à aucune portée lexicale dans le code , ou quoi que ce soit sur cet ordre.Pour tous ceux qui pourraient se soucier de la fiabilité de ce type de codage: si vous passez un appel téléphonique vers, depuis ou à travers presque n'importe quelle partie de l'Europe, il y a de fortes chances qu'il soit géré (au moins en partie) par code cela fait exactement cela.
la source
bool selfDelete
paramètre dans le constructeur qui est attribué à une variable membre. Certes, cela signifie remettre au programmeur suffisamment de corde pour y attacher un nœud coulant, mais je trouve cela préférable aux fuites de mémoire.this
. Oui, le code est géré par exactementthis
. ;)Si cela vous fait peur, il y a un hack parfaitement légal:
Je pense que
delete this
c'est du C ++ idiomatique, et je ne présente cela que comme une curiosité.Il existe un cas où cette construction est réellement utile - vous pouvez supprimer l'objet après avoir levé une exception qui a besoin des données de membre de l'objet. L'objet reste valide jusqu'à ce que le lancer ait lieu.
Remarque: si vous utilisez un compilateur plus ancien que C ++ 11 que vous pouvez utiliser à la
std::auto_ptr
place destd::unique_ptr
, il fera la même chose.la source
unique_ptr
partir d' un autreunique_ptr
nécessite un déplacement, mais pas à partir d'un pointeur brut. A moins que les choses ne changent en C ++ 17?L'une des raisons pour lesquelles C ++ a été conçu était de faciliter la réutilisation du code. En général, C ++ doit être écrit pour qu'il fonctionne, que la classe soit instanciée sur le tas, dans un tableau ou sur la pile. "Supprimer ceci" est une très mauvaise pratique de codage car cela ne fonctionnera que si une seule instance est définie sur le tas; et il vaut mieux ne pas avoir une autre instruction delete, qui est généralement utilisée par la plupart des développeurs pour nettoyer le tas. Cela suppose également qu'aucun programmeur de maintenance à l'avenir ne guérira 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 un développeur heureux arrive à l'avenir et décide de créer une instance sur la pile? Ou, que se passe-t-il s'il coupe et colle certaines parties de la classe dans une nouvelle classe qu'il a l'intention d'utiliser sur la pile? Lorsque le code atteint "supprimer ceci", il s'éteint et le supprime, mais lorsque l'objet sort de la portée, il appelle le destructeur. Le destructeur tentera alors de le supprimer à nouveau et vous serez arrosé. Dans le passé, faire quelque chose comme ça reviendrait à foutre non seulement le programme mais aussi le système d'exploitation et l'ordinateur à redémarrer. Dans tous les cas, cela est fortement déconseillé et devrait presque toujours être évité. Je devrais être désespéré, sérieusement enduit,
la source
Il est autorisé (n'utilisez pas l'objet après cela), mais je n'écrirais pas un tel code sur la pratique. Je pense que
delete this
devrait apparaître que dans les fonctions qui a appelérelease
ouRelease
et ressemble à :void release() { ref--; if (ref<1) delete this; }
.la source
Eh bien, dans la
delete this
construction COM (Component Object Model) peut être une partie de laRelease
méthode qui est appelée chaque fois que vous souhaitez libérer un objet acquis:la source
Il s'agit de l'idiome de base pour les objets comptés par référence.
Le comptage de références est une forme forte de collecte de déchets déterministe - il garantit que les objets gèrent leur propre durée de vie au lieu de compter sur des pointeurs «intelligents», etc. pour le faire pour eux. L'objet sous-jacent n'est accessible que via des pointeurs intelligents "Référence", conçus pour que les pointeurs incrémentent et décrémentent un entier membre (le nombre de références) dans l'objet réel.
Lorsque la dernière référence tombe de la pile ou est supprimée, le nombre de références passe à zéro. Le comportement par défaut de votre objet sera alors un appel à "supprimer ceci" pour le ramasse-miettes - les bibliothèques que j'écris fournissent un appel virtuel "CountIsZero" protégé dans la classe de base afin que vous puissiez remplacer ce comportement pour des choses comme la mise en cache.
La clé pour rendre ce coffre-fort n'est pas de permettre aux utilisateurs d'accéder au CONSTRUCTEUR de l'objet en question (le rendre protégé), mais plutôt de les faire appeler un membre statique - le "FACTORY" comme "Reference Reference CreateT (...)". De cette façon, vous SAVEZ avec certitude qu'ils sont toujours construits avec un "nouveau" ordinaire et qu'aucun pointeur brut n'est jamais disponible, donc "supprimer ceci" ne explosera jamais.
la source
Tu peux le faire. Cependant, vous ne pouvez pas l'affecter. Ainsi, la raison pour laquelle vous déclarez faire cela, "je veux changer la vue", semble très discutable. La meilleure méthode, à mon avis, serait que l'objet qui contient la vue remplace cette vue.
Bien sûr, vous utilisez des objets RAII et vous n'avez donc pas du tout besoin d'appeler supprimer ... n'est-ce pas?
la source
C'est une vieille question à laquelle il a répondu, mais @Alexandre a demandé "Pourquoi quelqu'un voudrait-il faire cela?", Et j'ai pensé que je pourrais fournir un exemple d'utilisation que j'envisage cet après-midi.
Code hérité. Utilise des pointeurs nus Obj * obj avec un obj de suppression à la fin.
Malheureusement, j'ai besoin parfois, pas souvent, de garder l'objet en vie plus longtemps.
J'envisage d'en faire un pointeur intelligent compté par référence. Mais il y aurait beaucoup de code à changer si je devais l'utiliser
ref_cnt_ptr<Obj>
partout. Et si vous mélangez Obj * et ref_cnt_ptr nus, vous pouvez supprimer l'objet implicitement lorsque le dernier ref_cnt_ptr disparaît, même si Obj * est toujours vivant.Je pense donc à créer un explicit_delete_ref_cnt_ptr. C'est-à-dire un pointeur compté par référence où la suppression n'est effectuée que dans une routine de suppression explicite. En l'utilisant au seul endroit où le code existant connaît la durée de vie de l'objet, ainsi que dans mon nouveau code qui maintient l'objet en vie plus longtemps.
L'incrémentation et la décrémentation du nombre de références lorsque explicit_delete_ref_cnt_ptr sont manipulées.
Mais PAS de libération lorsque le nombre de références est considéré comme nul dans le destructeur explicit_delete_ref_cnt_ptr.
Libération uniquement lorsque le nombre de références est considéré comme nul dans une opération de type suppression explicite. Par exemple dans quelque chose comme:
OK, quelque chose comme ça. Il est un peu inhabituel qu'un type de pointeur compté par référence ne supprime pas automatiquement l'objet pointé dans le destructeur ptr rc'ed. Mais il semble que cela puisse rendre un peu plus sûr le mélange des pointeurs nus et des pointeurs rc'ed.
Mais jusqu'à présent, pas besoin de supprimer cela.
Mais ensuite, cela m'est venu à l'esprit: si l'objet pointé, la pointee, sait qu'il est compté par référence, par exemple si le compte est à l'intérieur de l'objet (ou dans une autre table), alors la routine delete_if_rc0 pourrait être une méthode de la objet pointé, pas le pointeur (intelligent).
En fait, il ne doit pas du tout être une méthode membre, mais peut être une fonction libre:
(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.)
la source
Supprimer ceci est légal tant que l'objet est en tas. Vous auriez besoin d'exiger que l'objet soit un tas uniquement. La seule façon de le faire est de protéger le destructeur - de cette façon, delete peut être appelé UNIQUEMENT à partir de la classe, donc vous auriez besoin d'une méthode qui garantirait la suppression
la source