Question de base: quand un programme appelle-t-il une méthode destructrice de classe en C ++? On m'a dit qu'il est appelé chaque fois qu'un objet sort du champ d'application ou est soumis à undelete
Questions plus spécifiques:
1) Si l'objet est créé via un pointeur et que ce pointeur est ultérieurement supprimé ou donné une nouvelle adresse à pointer, est-ce que l'objet vers lequel il pointait appelle son destructeur (en supposant que rien d'autre ne pointe vers lui)?
2) Suite à la question 1, qu'est-ce qui définit quand un objet sort de la portée (pas en ce qui concerne le moment où un objet quitte un {bloc} donné). Donc, en d'autres termes, quand un destructeur est-il appelé sur un objet dans une liste chaînée?
3) Souhaitez-vous jamais appeler un destructeur manuellement?
la source
Réponses:
Cela dépend du type de pointeurs. Par exemple, les pointeurs intelligents suppriment souvent leurs objets lorsqu'ils sont supprimés. Les pointeurs ordinaires ne le font pas. La même chose est vraie lorsqu'un pointeur est amené à pointer vers un objet différent. Certains pointeurs intelligents détruiront l'ancien objet ou le détruiront s'il n'a plus de références. Les pointeurs ordinaires n'ont pas une telle intelligence. Ils détiennent simplement une adresse et vous permettent d'effectuer des opérations sur les objets qu'ils pointent en le faisant spécifiquement.
Cela dépend de la mise en œuvre de la liste chaînée. Les collections typiques détruisent tous les objets qu'elles contiennent lorsqu'elles sont détruites.
Ainsi, une liste liée de pointeurs détruirait généralement les pointeurs mais pas les objets vers lesquels ils pointent. (Ce qui peut être correct. Il peut s'agir de références par d'autres pointeurs.) Cependant, une liste chaînée spécialement conçue pour contenir des pointeurs peut supprimer les objets lors de sa propre destruction.
Une liste liée de pointeurs intelligents peut supprimer automatiquement les objets lorsque les pointeurs sont supprimés, ou le faire s’ils n’ont plus de références. C'est à vous de choisir les pièces qui font ce que vous voulez.
Sûr. Un exemple serait si vous voulez remplacer un objet par un autre objet du même type mais ne voulez pas libérer de la mémoire juste pour l'allouer à nouveau. Vous pouvez détruire l'ancien objet sur place et en construire un nouveau sur place. (Cependant, c'est généralement une mauvaise idée.)
la source
new Foo()
avec un 'F' majuscule.)Foo myfoo("foo")
n'est pas l'analyse la plus vexante, mais elle l'char * foo = "foo"; Foo myfoo(foo);
est.delete myFoo
être appelée avantFoo *myFoo = new Foo("foo");
? Ou bien vous supprimeriez l'objet nouvellement créé, non?myFoo
avant laFoo *myFoo = new Foo("foo");
ligne. Cette ligne crée une toute nouvelle variable appeléemyFoo
, ombrageant toute variable existante. Bien que dans ce cas, il n'y en ait pas, car ce quimyFoo
précède est dans le champ d'application duif
, qui a pris fin.D'autres ont déjà résolu les autres problèmes, alors je vais juste regarder un point: voulez-vous jamais supprimer manuellement un objet.
La réponse est oui. @DavidSchwartz a donné un exemple, mais c'est un exemple assez inhabituel. Je vais donner un exemple qui est sous le capot de ce que beaucoup de programmeurs C ++ utilisent tout le temps:
std::vector
(etstd::deque
, bien qu'il ne soit pas autant utilisé).Comme la plupart des gens le savent,
std::vector
allouera un plus grand bloc de mémoire lorsque / si vous ajoutez plus d'éléments que son allocation actuelle ne peut en contenir. Quand il le fait, cependant, il dispose d'un bloc de mémoire capable de contenir plus d' objets que ce qui est actuellement dans le vecteur.Pour gérer cela, ce qui
vector
fait sous couvertures est d'allouer de la mémoire brute via l'Allocator
objet (ce qui, sauf indication contraire, signifie qu'il utilise::operator new
). Ensuite, lorsque vous utilisez (par exemple)push_back
pour ajouter un élément àvector
, le vecteur utilise en interne aplacement new
pour créer un élément dans la partie (précédemment) inutilisée de son espace mémoire.Maintenant, que se passe-t-il quand / si vous
erase
un élément du vecteur? Il ne peut pas simplement utiliserdelete
- cela libérerait tout son bloc de mémoire; il doit détruire un objet dans cette mémoire sans en détruire d'autres, ni libérer aucun des blocs de mémoire qu'il contrôle (par exemple, si vouserase
5 éléments d'un vecteur, puis immédiatementpush_back
5 éléments supplémentaires, il est garanti que le vecteur ne se réallouera pas mémoire quand vous le faites.Pour ce faire, le vecteur détruit directement les objets de la mémoire en appelant explicitement le destructeur, et non en utilisant
delete
.Si, par hasard, quelqu'un d'autre devait écrire un conteneur en utilisant un stockage contigu à peu près comme un
vector
do (ou une variante de cela, comme le faitstd::deque
vraiment), vous voudrez presque certainement utiliser la même technique.Par exemple, considérons comment vous pourriez écrire du code pour un tampon circulaire circulaire.
Contrairement aux conteneurs standard, cela utilise
operator new
etoperator delete
directement. Pour une utilisation réelle, vous souhaitez probablement utiliser une classe d'allocateur, mais pour le moment, cela ferait plus de distraire que de contribuer (IMO, en tout cas).la source
new
, vous êtes responsable de l'appeldelete
. Lorsque vous créez un objet avecmake_shared
, le résultatshared_ptr
est chargé de tenir compte et d'appelerdelete
lorsque le nombre d'utilisation passe à zéro.new
(c'est-à-dire qu'il s'agit d'un objet de pile).new
l'objet .la source
1) Les objets ne sont pas créés «via des pointeurs». Un pointeur est affecté à tout objet que vous «nouveau». En supposant que c'est ce que vous voulez dire, si vous appelez 'delete' sur le pointeur, cela supprimera (et appellera le destructeur) l'objet que le pointeur déréférencera. Si vous affectez le pointeur à un autre objet, il y aura une fuite de mémoire; rien en C ++ ne collectera vos déchets pour vous.
2) Ce sont deux questions distinctes. Une variable sort de la portée lorsque le cadre de pile dans lequel elle est déclarée est sorti de la pile. C'est généralement lorsque vous quittez un bloc. Les objets d'un tas ne sont jamais hors de portée, bien que leurs pointeurs sur la pile le puissent. Rien en particulier ne garantit qu'un destructeur d'un objet dans une liste chaînée sera appelé.
3) Pas vraiment. Il peut y avoir Deep Magic qui suggérerait le contraire, mais généralement vous voulez faire correspondre vos "nouveaux" mots-clés avec vos mots-clés "supprimer", et mettre tout ce qui est nécessaire dans votre destructeur pour vous assurer qu'il se nettoie correctement. Si vous ne le faites pas, assurez-vous de commenter le destructeur avec des instructions spécifiques à toute personne utilisant la classe sur la façon dont elle doit nettoyer manuellement les ressources de cet objet.
la source
Pour donner une réponse détaillée à la question 3: oui, il y a de (rares) occasions où vous pourriez appeler explicitement le destructeur, notamment en tant que contrepartie d'un placement nouveau, comme l'observe dasblinkenlight.
Pour en donner un exemple concret:
Le but de ce genre de chose est de découpler l'allocation de mémoire de la construction d'objets.
la source
Pointeurs - Les pointeurs réguliers ne prennent pas en charge RAII. Sans explicite
delete
, il y aura des déchets. Heureusement, C ++ a des pointeurs automatiques qui gèrent cela pour vous!Portée - Pensez au moment où une variable devient invisible pour votre programme. Habituellement, c'est à la fin
{block}
, comme vous le faites remarquer.Destruction manuelle - N'essayez jamais cela. Laissez simplement scope et RAII faire la magie pour vous.
la source
std::auto_ptr
est obsolète en C ++ 11, oui. Si l'OP a réellement C ++ 11, il doit l'utiliserstd::unique_ptr
pour des propriétaires uniques oustd::shared_ptr
pour plusieurs propriétaires comptés par référence.std::queue<std::shared_ptr>?
j'ai trouvépipe()
qu'entre un thread producteur et consommateur rendait la concurrence beaucoup plus facile, si la copie n'est pas trop chère.Chaque fois que vous utilisez «nouveau», c'est-à-dire que vous attachez une adresse à un pointeur, ou pour dire que vous revendiquez de l'espace sur le tas, vous devez le «supprimer».
1. oui, lorsque vous supprimez quelque chose, le destructeur est appelé.
2.Lorsque le destructeur de la liste chaînée est appelé, le destructeur de ses objets est appelé. Mais s'il s'agit de pointeurs, vous devez les supprimer manuellement. 3.lorsque l'espace est réclamé par "nouveau".
la source
Oui, un destructeur (aka dtor) est appelé lorsqu'un objet sort de la portée s'il est sur la pile ou lorsque vous appelez
delete
un pointeur vers un objet.Si le pointeur est supprimé via,
delete
le dtor sera appelé. Si vous réaffectez le pointeur sans appeler d'delete
abord, vous obtiendrez une fuite de mémoire car l'objet existe toujours en mémoire quelque part. Dans ce dernier cas, le dtor n'est pas appelé.Une bonne implémentation de liste chaînée appellera le dtor de tous les objets de la liste lorsque la liste est détruite (parce que soit vous avez appelé une méthode pour la destituer, soit elle est sortie de sa portée elle-même). Cela dépend de la mise en œuvre.
J'en doute, mais je ne serais pas surpris s'il y avait des circonstances étranges là-bas.
la source
Si l'objet n'est pas créé via un pointeur (par exemple, A a1 = A ();), le destructeur est appelé lorsque l'objet est détruit, toujours lorsque la fonction où se trouve l'objet est terminée, par exemple:
le destructeur est appelé lorsque le code est exécuté sur la ligne "terminer".
Si l'objet est créé via un pointeur (par exemple, A * a2 = new A ();), le destructeur est appelé lorsque le pointeur est supprimé (delete a2;). Si le point n'est pas supprimé explicitement par l'utilisateur ou s'il reçoit un nouvelle adresse avant de la supprimer, la fuite de mémoire s'est produite. C'est un bug.
Dans une liste chaînée, si nous utilisons std :: list <>, nous n'avons pas besoin de nous soucier du desctructor ou de la fuite de mémoire car std :: list <> a terminé tout cela pour nous. Dans une liste chaînée écrite par nous-mêmes, nous devons écrire le desctructor et supprimer explicitement le pointeur, sinon cela entraînera une fuite de mémoire.
Nous appelons rarement un destructeur manuellement. C'est une fonction assurant le système.
Désolé pour mon mauvais anglais!
la source
Rappelez-vous que le constructeur d'un objet est appelé immédiatement après l'allocation de la mémoire pour cet objet et que le destructeur est appelé juste avant de désallouer la mémoire de cet objet.
la source