La destruction des objets est un problème parfois délicat. Comme on peut définir ses propres constructeurs, il faut aussi définir le moyen de détruire ce qu'ils ont créé. C'est le cas, par exemple, quand le constructeur fait de l'allocation dynamique.
Le compilateur génère automatiquement le ``code'' nécessaire à la destruction de l'objet quand cet objet n'est plus accessible, comme, par exemple, pour un objet local à une fonction après le retour dans le programme appelant. Par défaut (en l'absence d'un destructeur), le compilateur n'a rien à faire, l'allocation ayant eu lieu sur la pile d'exécution.
Un destructeur est une fonction membre de nom
~X
(X
étant le nom de la classe). Son but est de
détruire les valeurs de type X
avant que l'objet
les contenant ne le soit. Un destructeur est l'inverse
d'un constructeur.
// tableau d'entiers class tab_ent{ public: tab_ent(int n){ t=new int[taille=n]; assert(t != 0); } ~tab_ent(){ delete [] t; } //... private: int *t; int taille; }Si un destructeur n'avait pas été défini pour
tab_ent
, quand un objet tab_ent
est
détruit, l'allocation qui avait été réalisée pour
le membre t
reste.
Un destructeur est appelé quand:
auto
ou une variable temporairedelete
pour les objets alloués par
new
Pour des raisons évidentes, le corps d'un destructeur est exécuté avant les destructeurs pour les objets membres (ordre inverse des constructeurs).
Les destructeurs peuvent être virtuels, mais ils ne le
sont pas par défaut.
La gestion dynamique de la mémoire est un problème délicat. Pour des raisons d'efficacité, il faut éviter la recopie, mais alors il faut définir une stratégie pour libérer la mémoire...
struct item{ char *data; item *suivant; //... }; item a; class liste{ // public: //... ~liste(); };Que doit faire le
~liste()
, doit-il libérer les data
des
item
: si oui cela suppose qu'il n'y a pas de partage et qu'on n'a pas
eu d'affectation du genre a.data="bonjour"