2010-10-27 17 views
6

J'ai ce simple code C++, mais je ne sais pas comment utiliser le destructeur.C++ code destructeur

class date { 

public: 
    int day; 
    date(int m) 
    { 
     day =m; 
    } 

    ~date(){ 
    cout << "I wish you have entered the year \n" << day;  
    } 
}; 


int main() 
{ 
    date ob2(12); 
    ob2.~date(); 
    cout << ob2.day; 
    return 0; 
} 

la question est, que dois-je écrire dans mon code destructor, qui après avoir appelé la destructor, il supprimera la variable « jour ». ???

+0

Beaucoup de réponses font référence à "la pile". Si vous n'êtes pas sûr de ce qu'est la pile, lisez cette page: http://web.archive.org/web/20071029040931/www.dirac.org/linux/gdb/02a-Memory_Layout_And_The_Stack.php –

Répondre

1

Vous ne devez pas appeler votre destructeur explicitement.

Lorsque vous créez votre objet sur la pile (comme vous l'avez fait) tout ce que vous avez besoin est:

int main() 
{ 
    date ob2(12); 
    // ob2.day holds 12 
    return 0; // ob2's destructor will get called here, after which it's memory is freed 
} 

Lorsque vous créez votre objet sur le tas, vous avez besoin d'un peu de delete votre classe avant son destructor est appelé et la mémoire est libérée:

int main() 
{ 
    date* ob2 = new date(12); 
    // ob2->day holds 12 
    delete ob2; // ob2's destructor will get called here, after which it's memory is freed 
    return 0; // ob2 is invalid at this point. 
} 

(a défaut d'appeler à supprimer ce dernier exemple se traduira par une perte de mémoire.)

les deux méthodes ont leurs avantages un d inconvénients. Le chemin de pile est TRES RAPIDE avec l'allocation de la mémoire que l'objet va occuper et vous n'avez pas besoin de le supprimer explicitement, mais la pile a un espace limité et vous ne pouvez pas déplacer ces objets facilement, rapidement et proprement. Le tas est la manière préférée de le faire, mais quand il s'agit de la performance, il est lent à allouer et vous devez faire face à des pointeurs. Mais vous avez beaucoup plus de flexibilité avec ce que vous faites avec votre objet, il est beaucoup plus rapide de travailler avec des pointeurs plus loin et vous avez plus de contrôle sur la durée de vie de l'objet.

+2

"Le tas est la façon préférée de le faire" - faire * quoi *? Stocker des objets? Parce que ce serait faux: la pile est absolument le moyen préféré de le faire en C++. –

+0

En travaillant avec des bibliothèques et en leur passant des objets, il est préférable (non, vital) de les affecter sur le tas. Bien sûr, la pile est rapide et sale et, dans certains cas, logique. Mais quand vous avez des objets que vous voulez utiliser, passez, déplacez, copiez et référencez le nombre, alors la pile n'est pas un endroit pour eux. Jusqu'à ce que la sémantique de déplacement de C++ 0x devienne banale c'est ... – Marius

+1

désolé mais c'est faux. Pour argument-passing, utilisez les références (const). Pour les valeurs de retour - simplement copier. Rien de mal n'arrivera. Voir par exemple http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/ Les bibliothèques ne devraient jamais exiger d'arguments alloués par tas. Ce serait un ** très ** mauvais design de bibliothèque. Et il n'y a rien de rapide et de sale à propos de l'allocation des piles. –

9

Vous n'avez pas besoin d'appeler le destructeur explicitement. Ceci est fait automatiquement à la fin de la portée de l'objet ob2, c'est-à-dire à la fin de la fonction main.

De plus, étant donné que l'objet possède un stockage automatique, son stockage n'a pas besoin d'être supprimé. Cela aussi est fait automatiquement à la fin de la fonction.

L'appel manuel des destructeurs n'est presque jamais nécessaire (uniquement dans le code de bibliothèque de bas niveau) et la suppression manuelle de la mémoire est nécessaire (et uniquement valide) lorsque la mémoire a été précédemment acquise avec new (lorsque vous utilisez des pointeurs).

Étant donné que la gestion manuelle de la mémoire est sujette à des fuites, le code C++ moderne essaie de ne pas utiliser explicitement new et delete. Quand il est vraiment nécessaire d'utiliser new, alors un "pointeur intelligent" est utilisé à la place d'un pointeur régulier.

+0

Tout cela est bien et bien, mais je crois que l'affiche a une question beaucoup plus fondamentale. – Marius

+0

Légère clarification sur votre troisième paragraphe: l'appel explicite de destructeurs peut être acceptable (quoique laid et souvent évitable) lors de l'utilisation de l'emplacement new. – Sydius

+0

@Sydius: c'est pourquoi j'ai écrit "seulement dans le code de bibliothèque de bas niveau". Ne confondons pas l'OP en jetant encore plus de notations avancées. –

14

Rarement avez-vous besoin d'appeler le destructeur explicitement. Au lieu de cela, le destructeur est appelé lorsqu'un objet est détruit.

Pour un objet comme ob2 qui est une variable locale, il est détruit quand il est hors de portée:

int main() 
{ 
    date ob2(12); 

} // ob2.~date() is called here, automatically! 

Si vous allouez dynamiquement un objet en utilisant new, son destructor est appelée lorsque l'objet est détruit en utilisant delete. Si vous avez un objet statique, son destructeur est appelé lorsque le programme se termine (si le programme se termine normalement).

À moins que vous créez quelque chose en utilisant dynamiquement new, vous n'avez pas besoin de faire quoi que ce soit explicite pour le nettoyer (ainsi, par exemple, lorsque ob2 est détruit, toutes ses variables membres, y compris day, sont détruits). Si vous créez quelque chose dynamiquement, vous devez vous assurer qu'il est détruit lorsque vous avez terminé; la meilleure pratique consiste à utiliser ce qu'on appelle un «pointeur intelligent» pour s'assurer que ce nettoyage est géré automatiquement.

0

Dans ce cas, votre destructeur n'a pas besoin de supprimer la variable de jour.

Vous avez seulement besoin d'appeler delete sur la mémoire que vous avez allouée avec new.

Voilà comment votre code ressemblerait si vous utilisez et d'en supprimer pour déclencher l'invocation du destructor

class date { 

    public: int* day; 
    date(int m) { 
     day = new int; 
     *day = m; 
    } 

    ~date(){ 
     delete day; 
     cout << "now the destructor get's called explicitly"; 
    } 
}; 

int main() { 
    date *ob2 = new date(12); 
    delete ob2; 
    return 0; 
} 
+0

Outre le fait que le code alloue de la mémoire sur le tas pour stocker un entier, quoi d'autre ne va pas avec le style? –

+0

c'est ce que je voulais dire. Mais en plus (comme je l'ai souligné dans mon article ci-dessous), toute utilisation de 'new' en conjonction avec des pointeurs bruts est quelque peu déconseillée dans le C++ moderne car cela conduit à beaucoup de problèmes. Certainement pour les débutants. –

+0

Le code a le problème supplémentaire de ne pas avoir un constructeur de copie et un opérateur d'assignation de copie correctement définis, ce qui est un piège extrêmement commun pour les débutants. –

2

que dans des circonstances très spécifiques que vous devez appeler directement l'destructor. Par défaut, le destructeur sera appelé par le système lorsque vous créez une variable de stockage automatique et qu'il est hors de portée ou lorsqu'un objet dynamiquement alloué avec new est détruit avec delete.

struct test { 
    test(int value) : value(value) {} 
    ~test() { std::cout << "~test: " << value << std::endl; } 
    int value; 
}; 
int main() 
{ 
    test t(1); 
    test *d = new t(2); 
    delete d;   // prints: ~test: 2 
}      // prints: ~test: 1 (t falls out of scope) 

Pour être complet, (cela ne devrait pas être utilisé en général) la syntaxe pour appeler la destructor est similaire à une méthode. Après l'destructor est exécuté, la mémoire n'est plus un objet de ce type (doivent être traités comme la mémoire brute):

int main() 
{ 
    test t(1); 
    t.~test();   // prints: ~test: 1 
         // after this instruction 't' is no longer a 'test' object 
    new (&t) test(2);  // recreate a new test object in place 
}      // test falls out of scope, prints: ~test: 2 

Remarque: après avoir appelé la destructor sur t, cet emplacement de mémoire ne soit plus un test, c'est la raison pour récréation de l'objet au moyen de l'emplacement nouveau.

+0

La manière habituelle d'allouer de la mémoire à utiliser avec le placement new est avec malloc (ou new avec un tableau). C'est le premier exemple que je vois avec la variable stack. btw dans le 1er exemple, ce serait test * d = new test (2); –

+0

@VJo: J'ai commencé à écrire un exemple avec une mémoire tampon allouée par pile, mais cela nécessitait l'utilisation de placement new * avant * d'appeler le destructeur (petit problème) et n'affichait pas le problème potentiel de plusieurs appels au destructeur. Il y a aussi un autre exemple * auto * attribué dans la nouvelle norme: avec les exigences moindres placées sur les membres d'une union, elle permet des objets avec des constructeurs/destructeurs non triviaux. Dans ce cas, il appartient à l'utilisateur d'appeler explicitement le constructeur et le destructeur du membre actif de l'union. –

0

Même si le destructeur semble être quelque chose que vous devez appeler pour vous débarrasser ou «détruire» votre objet lorsque vous avez fini de l'utiliser, vous n'êtes pas censé l'utiliser de cette façon. Le destructeur est quelque chose qui est automatiquement appelé lorsque votre objet sort de la portée, c'est-à-dire lorsque l'ordinateur quitte les "accolades" dans lesquelles vous avez instancié votre objet. Dans ce cas, lorsque vous quittez main(). Vous ne voulez pas l'appeler vous-même.

0

Vous pouvez être dérouté par un comportement indéfini ici. La norme C++ n'a aucune règle quant à ce qui se passe si vous utilisez un objet après que son destructeur a été exécuté, car ce comportement n'est pas défini, et par conséquent l'implémentation peut faire tout ce qu'elle veut. Généralement, les concepteurs de compilateurs ne font rien de spécial pour un comportement indéfini, et donc ce qui se passe est un artefact de ce que d'autres décisions de conception ont été prises. (Cela peut parfois donner des résultats vraiment bizarres.)

Par conséquent, une fois que vous avez exécuté le destructeur, le compilateur n'a plus aucune obligation concernant cet objet. Si vous ne vous y référez pas, cela n'a pas d'importance. Si vous vous y référez, c'est un comportement indéfini, et du point de vue du standard, le comportement n'a pas d'importance, et puisque la norme ne dit rien, la plupart des concepteurs de compilateurs ne se soucieront pas de ce que fait le programme. Dans ce cas, la chose la plus simple à faire est de laisser l'objet intact, car il ne tient pas sur les ressources, et son stockage a été alloué lors du démarrage de la fonction et ne sera pas récupéré tant que la fonction sorties. Par conséquent, la valeur du membre de données restera la même.La chose naturelle pour le compilateur à faire quand il lit ob2.day est d'accéder à l'emplacement de la mémoire.

Comme tout autre exemple de comportement indéfini, les résultats peuvent changer en cas de changement de circonstances, mais dans ce cas, ils ne le seront probablement pas. Ce serait bien si les compilateurs attrapent plus de cas de comportement indéfini et de diagnostic, mais il est impossible pour les compilateurs de détecter tout comportement indéfini (certains surviennent à l'exécution) et souvent ils ne vérifient pas le comportement qu'ils ne pensent pas. probable.