Il fera la même chose (rien, en gros). Mais ce n'est pas la même chose que si vous ne l'aviez pas écrit. Parce que pour écrire le destructeur, il faut que le destructeur de la classe de base fonctionne. Si le destructeur de la classe de base est privé ou s'il y a une autre raison pour laquelle il ne peut pas être invoqué, alors votre programme est défectueux. Considérez ceci
struct A { private: ~A(); };
struct B : A { };
C'est OK, tant que vous n'avez pas besoin de déstructurer un objet de type B (et donc, implicitement, de type A) - comme si vous n'appelez jamais delete sur un objet créé dynamiquement, ou si vous ne créez jamais un objet de ce type en premier lieu. Si vous le faites, alors le compilateur affichera un diagnostic approprié. Maintenant, si vous en fournissez un explicitement
struct A { private: ~A(); };
struct B : A { ~B() { /* ... */ } };
Celle-ci tentera d'appeler implicitement le destructeur de la classe de base, et provoquera un diagnostic déjà au moment de la définition de ~B
.
Il existe une autre différence qui concerne la définition du destructeur et les appels implicites aux destructeurs membres. Considérons ce membre pointeur intelligent
struct C;
struct A {
auto_ptr<C> a;
A();
};
Supposons que l'objet de type C
est créé dans la définition du constructeur de A dans le fichier .cpp
qui contient également la définition de la structure C
. Maintenant, si vous utilisez la structure A
et exiger la destruction d'un A
le compilateur fournira une définition implicite du destructeur, comme dans le cas ci-dessus. Ce destructeur appellera aussi implicitement le destructeur de l'objet auto_ptr. Et cela supprimera le pointeur qu'il détient, qui pointe sur l'objet C
sans connaître la définition de l'objet C
! Cela est apparu dans le .cpp
où le constructeur de la structure A est défini.
Il s'agit en fait d'un problème courant dans la mise en œuvre de l'idiome pimpl. La solution ici est d'ajouter un destructeur et de fournir une définition vide de celui-ci dans le fichier .cpp
où la structure C
est défini. Au moment où il invoquera le destructeur de son membre, il connaîtra alors la définition de la structure C
et peut appeler correctement son destructeur.
struct C;
struct A {
auto_ptr<C> a;
A();
~A(); // defined as ~A() { } in .cpp file, too
};
Notez que boost::shared_ptr
n'a pas ce problème : il exige au contraire un type complet lorsque son constructeur est invoqué de certaines manières.
Un autre point où cela fait une différence dans le C++ actuel est lorsque vous voulez utiliser memset
et amis sur un tel objet qui a un destructeur déclaré par l'utilisateur. De tels types ne sont plus des PODs (plain old data), et ils ne sont pas autorisés à être copiés en bits. Notez que cette restriction n'est pas vraiment nécessaire - et la prochaine version de C++ a amélioré la situation à ce sujet, de sorte qu'elle vous permet de copier en bits de tels types, tant que d'autres changements plus importants ne sont pas effectués.
Puisque vous avez demandé des constructeurs : Eh bien, pour ceux-ci, les mêmes choses sont vraies. Notez que les constructeurs contiennent également des appels implicites aux destructeurs. Sur des choses comme auto_ptr, ces appels (même s'ils ne sont pas réellement effectués à l'exécution - la pure possibilité compte déjà ici) feront le même mal que pour les destructeurs, et se produisent lorsque quelque chose dans le constructeur jette - le compilateur est alors tenu d'appeler le destructeur des membres. Cette réponse fait un certain usage de la définition implicite des constructeurs par défaut.
De plus, la même chose est vraie pour la visibilité et la PODness que ce que j'ai dit à propos du destructeur ci-dessus.
Il y a une différence importante concernant l'initialisation. Si vous mettez un constructeur déclaré par l'utilisateur, votre type ne reçoit plus d'initialisation de valeur des membres, et c'est à votre constructeur de faire toute initialisation nécessaire. Exemple :
struct A {
int a;
};
struct B {
int b;
B() { }
};
Dans ce cas, ce qui suit est toujours vrai
assert(A().a == 0);
Alors que ce qui suit est un comportement non défini, car b
n'a jamais été initialisé (votre constructeur l'a omis). La valeur peut être zéro, mais peut tout aussi bien être n'importe quelle autre valeur bizarre. Essayer de lire à partir d'un tel objet non initialisé provoque un comportement non défini.
assert(B().b == 0);
Cela vaut également pour l'utilisation de cette syntaxe en new
comme new A()
(notez les parenthèses à la fin - si elles sont omises, l'initialisation de la valeur n'est pas faite, et comme il n'y a pas de constructeur déclaré par l'utilisateur qui pourrait l'initialiser, a
sera laissé non initialisé).
0 votes
J'ai un peu modifié cette question pour que l'édition de l'afterthoguht devienne une partie réelle de la question. S'il y a des erreurs de syntaxe dans les parties que j'ai modifiées, criez sur moi, pas sur l'auteur de la question originale. @Andrew, si vous avez l'impression que j'ai trop modifié votre question, n'hésitez pas à revenir en arrière ; si vous aimez le changement mais pensez que ce n'est pas suffisant, vous êtes évidemment le bienvenu pour modifier votre propre question.