web-dev-qa-db-fra.com

Destructeur C ++ par défaut

Lorsque je ne déclare pas un constructor par exemple, le compilateur me fournira un default constructor qui n'aura pas d'arguments et pas de définition (corps), et donc, prendra aucune action.

Si maintenant je ne déclare pas un destructor, le compilateur me fournira un default destructor sans définition (corps), et donc, je pense sans action.

Donc, si j'ai fini avec un objet par exemple, le default destructor réallouer la mémoire (libre) utilisée par l'objet? Si ce n'est pas le cas, pourquoi l'obtenons-nous?

Et, peut-être que la même question s'applique au default constructor. S'il ne fait rien, pourquoi est-il créé pour nous par défaut?

Merci.

47
Simplicity

Il est faux de dire qu'un constructeur par défaut généré par le compilateur ne prend aucune action. Il est équivalent à un constructeur défini par l'utilisateur avec un corps vide et une liste d'initialisation vide, mais cela ne signifie pas qu'il ne prend aucune mesure. Voici ce qu'il fait:

  1. Il appelle le constructeur par défaut de la classe de base.
  2. Il initialise le pointeur vtable, si la classe est polymorphe.
  3. Il appelle les constructeurs par défaut de tous les membres qui en ont. S'il y a un membre avec certains constructeurs, mais sans celui par défaut, c'est une erreur de compilation.

Et seulement si une classe n'est pas polymorphe, n'a pas de classe de base et n'a pas de membres qui nécessitent une construction, alors un constructeur par défaut généré par le compilateur ne fait rien. Mais même alors, un constructeur par défaut est parfois nécessaire pour les raisons expliquées dans d'autres réponses.

Il en va de même pour le destructeur - il appelle le destructeur des classes de base et les destructeurs de tous les membres qui les possèdent, il n'est donc pas vrai dans le cas général qu'un destructeur généré par le compilateur ne fait rien.

Mais l'allocation de mémoire n'a vraiment rien à voir avec cela. La mémoire est allouée avant l'appel du constructeur et elle n'est libérée qu'après la fin du dernier destructeur.

65
Sergei Tachenov

Parce que si vous n'avez aucun constructeur ou destructeur (accessible au public), alors un objet de la classe ne peut pas être instancié. Considérer:

class A
{
private:
    A() {}
    ~A() {}
};

A a;  // Oh dear!  Compilation error

Si vous ne déclarez explicitement aucun constructeur ou destructeur, le compilateur doit en fournir un pour permettre la création d'objets.

5
Oliver Charlesworth

Le destructeur par défaut ne fera rien (comme un constructeur par défaut).

Vous devrez en définir un vous-même, si votre destructeur a réellement besoin de faire quelque chose (par exemple: libérer des ressources).

Notez que vous devez généralement suivre la règle de trois : si votre programme doit faire quelque chose dans son destructeur (par exemple: libérer des ressources), vous devez également fournir un constructeur de copie et un opérateur d'affectation; C++ en fournit également des versions par défaut (qui, là encore, ne feront rien).

Le constructeur/destructeur/opérateur d'affectation/constructeur de copie par défaut est utile lorsque vous manipulez des classes simples où vous n'avez rien à faire. Un cas particulier est le POD: ils (avant C++ 0x) ne peuvent même pas avoir de constructeurs ou de destructeurs explicites.

4
peoro

Le constructeur et les destructeurs par défaut ne sont qu'une marchandise au cas où vous n'avez besoin de rien de spécial fait avec votre classe, vous n'avez pas besoin d'écrire une version vide manuellement. Ceci est commun aux autres langages OO, par exemple en Java vous n'avez pas besoin de fournir un constructeur si l'initialisation zéro des membres suffit. En même temps c'est une exigence de compatibilité descendante avec C. Si vous avez un struct en C, il n'aura ni constructeur ni destructeur (C n'a pas ces concepts), pour pouvoir gérer ce code en C++ qui a être un code valide.

Une autre option aurait été de déclarer dans le langage qu'une classe ne pouvait avoir ni constructeur ni destructeur, mais alors la spécification du langage entier devrait traiter le fait que certains types pourraient avoir des constructeurs et des destructeurs tandis que d'autres n'en ont pas, et cela fera la langue est plus complexe et plus difficile à spécifier. Le fait d'avoir des versions définies implicitement ne change pas le comportement et facilite la spécification.

À ce niveau, c'est comme si le terme overrider était appliqué à la méthode dans la classe de base. Dans la classe de base, il ne override rien, il n'y a rien à surcharger! Et pourtant, le langage déclare explicitement qu'une méthode virtuelle non pure déclarée dans une base est un substitut. Cela permet à la spécification de simplement dire que le final overrider sera appelé lorsque la méthode est appelée via un pointeur ou une référence sans avoir à ajouter un extre * ou l'implémentation de la méthode de base s'il n'y a pas de substitution pour ce particulier méthode dans cette hiérarchie particulière.

Lors de l'utilisation de pointeurs intelligents, le destructeur par défaut (voir la réponse de Sergey) peut être essentiel pour éviter les fuites de mémoire. Voici un exemple:

#include <iostream>
#include <memory>

using namespace std;

class Foo {
public:
  Foo(int n = 0): n(n) { cout << "Foo(" << n << ")" << endl; }
  ~Foo() { cout << "~Foo(" << n << ")" << endl; }
private:
  int n;
};

// notes:
// * default destructor of Bar calls destructors of unique_ptr<Foo> foo
//  and of unique_ptr<Foo[]> foo3, which, in turn, delete the Foo objects
// * foo2's Foo object leaks
class Bar {
public:
  Bar(): foo(new Foo(1)), foo2(new Foo(2)), foo3(new Foo[2]) { }
private:
  unique_ptr<Foo> foo;
  Foo* foo2;
  unique_ptr<Foo[]> foo3;
};

int main() {
  Bar bar;
  cout << "in main()" << endl;
}

Voici la sortie, montrant qu'une fuite ne se produit que pour foo2:

Foo(1)
Foo(2)
Foo(0)
Foo(0)
in main()
~Foo(0)
~Foo(0)
~Foo(1)
2
Ulrich Stern

La réponse courte est qu'en C++ chaque objet a besoin d'un constructeur et d'un destructeur, même s'il ne fait rien. Le compilateur les créant pour vous en arrière-plan satisfait donc cette exigence.

Une réponse plus longue est que les constructeurs sont responsables de l'initialisation des membres de la classe. Le constructeur par défaut effectue l'initialisation par défaut de tous les membres. (Cela ne signifie rien pour les types POD, mais les autres classes reçoivent leurs constructeurs par défaut.)

1
SoapBox

Un destructeur par défaut n'aurait aucun moyen de savoir quelle mémoire votre classe "possède" pour pouvoir la libérer.

Quant à la partie constructeur par défaut, je citerai le article Wikipedia sur celui-ci ...

En C++, les constructeurs par défaut sont importants car ils sont automatiquement invoqués dans certaines circonstances:

  • Lorsqu'une valeur d'objet est déclarée sans liste d'arguments, par ex. MyClass x ;; ou alloués dynamiquement sans liste d'arguments, par ex. nouvelle MyClass; le constructeur par défaut est utilisé pour initialiser l'objet
  • Lorsqu'un tableau d'objets est déclaré, par ex. MyClass x [10] ;; ou alloués dynamiquement, par ex. nouvelle MyClass [10]; le constructeur par défaut est utilisé pour initialiser tous les éléments
  • Lorsqu'un constructeur de classe dérivée n'appelle pas explicitement le constructeur de classe de base dans sa liste d'initialisation, le constructeur par défaut de la classe de base est appelé
  • Lorsqu'un constructeur de classe n'appelle pas explicitement le constructeur d'un de ses champs à valeur d'objet dans sa liste d'initialisation, le constructeur par défaut de la classe du champ est appelé
  • Dans la bibliothèque standard, certains conteneurs "remplissent" les valeurs en utilisant le constructeur par défaut lorsque la valeur n'est pas donnée explicitement, par exemple vecteur (10); initialise le vecteur avec 10 éléments, qui sont remplis avec la valeur construite par défaut de notre type.

Dans les circonstances ci-dessus, c'est une erreur si la classe n'a pas de constructeur par défaut. Le compilateur définira implicitement un constructeur par défaut

si aucun constructeur n'est explicitement défini pour une classe. Ce constructeur par défaut déclaré implicitement est équivalent à un constructeur par défaut défini avec un corps vide. (Remarque: si certains constructeurs sont définis, mais qu'ils ne sont pas tous par défaut, le compilateur ne définira pas implicitement un constructeur par défaut. Cela signifie qu'un constructeur par défaut peut ne pas exister pour une classe.)

0
Andrew White