Pourquoi est-ce destructeur virtuel déclencher un externe non résolu?
De considérer les éléments suivants:
Dans X. h:
class X
{
X();
virtual ~X();
};
X.cpp:
#include "X.h"
X::X()
{}
Essayer de construire cette (je suis en utilisant un .dll cible pour éviter une erreur sur le manque principal, et je suis en utilisant Visual Studio 2010):
Erreur 1 erreur LNK2001: symbole externe "privé virtuel: __thiscall X::~X(void)" (??1X@@EAE@XZ)
De petites modifications suite à un succès, cependant:
X. h:
class X
{
inline X(); //Now inlined, and everything builds
virtual ~X();
};
ou
X. h:
class X
{
X();
~X(); //No longer virtual, and everything builds
};
Quelles sont les causes de l'externe non résolu dans l'éditeur de liens lors de la .dtor est virtuel ou lorsque l' .ctor n'est pas inline?
EDIT:
Ou, peut-être plus intéressant, pourquoi dois-je pas obtenir un externe non résolu, si je fais le destructeur non-virtuel, ou si je inline le constructeur?
- Je suis sûr que je comprends la question/variantes, mais la ligne de fond est: si ça va être appelé, il a besoin d'une définition. Dans votre première variante, vous ne jamais définir le destructeur, qui est exactement ce que le message d'erreur indique. Je ne vois pas comment les variations de la construire, car il n'y a pas encore de définition d'un constructeur ou un destructeur que je peux voir.
- Pourtant, ils le font. Donc ma confusion. Il est assez simple de code de l'échantillon, et aucun de mes collègues a une réponse définitive à ce problème. La meilleure supposition est que quelque chose d'indéfini et nous nous retrouvons dans vendeur-territoire spécifique, mais je suis curieux de savoir ce qui précisément est pas défini qui déclencherait les différences de comportements.
- Imposable à dire, sauf si vous nous montrer tout ce qu'on vous dit!
- York: C'est tout ce que je suis en train de construire! Littéralement! Ok, j'ai peut-être omis un retour chariot en face de l'un des fichiers, mais sérieusement, c'est tout le projet. Vous pourriez les copier dans votre propre zéro de la bibliothèque en 20 secondes. C'est cette mort simple.
- Dans la première, il a besoin de l'adresse de l'destructeur pour construire la table virtuelle. Dans la seconde: Puisque le constructeur n'est jamais vraiment construit jamais besoin de construire la table virtuelle et n'a donc pas besoin de l'adresse du destructeur. Dans le troisième, nous n'avons pas besoin de l'adresse de l'destructeur lors de la construction du constructeur (comme il n'est plus dans la table virtuelle);.
Vous devez vous connecter pour publier un commentaire.
Situation 1:
Vous avez le code pour le constructeur.
Donc, il construit le constructeur dans le fichier objet. Le constructeur a besoin de l'adresse de l'destructeur à mettre dans la table virtuelle, car il ne peut pas trouver le constructeur ne peut pas être construit.
Situation 2: (inline constructeur)
Le compilateur décide qu'il n'est pas nécessaire de construire le constructeur (comme elle est insérée).
Comme tel, il ne plante pas tout le code, et n'a donc pas besoin de l'adresse de l'destructeur.
Si vous instancier un objet de type X il va encore se plaindre.
Situation 3: (non destructeur virtuel)
Vous n'avez pas besoin de l'adresse de l'destructeur pour construire le constructeur.
Donc, il ne se plaint pas.
Il va se plaindre si vous instancier un objet de type X.
Vous avez besoin de donner un corps à l'destructeur virtuel:
En C++ fonctions doivent être défini si et seulement si ils sont utilisé dans votre programme (voir l'ODR en 3.2/2). En général, les fonctions virtuelles sont utilisé si elles sont appelées à partir potentiellement d'évaluation des expressions. Tout non-fonction virtuelle pure est considéré comme inconditionnellement utilisé. Lorsque [non virtuelle] spécial des fonctions membres sont utilisé est définie dans un lieu dédié à la langue standard. Et ainsi de suite.
Dans votre première exemple, avez-vous déclaré votre destructeur comme un non-fonction virtuelle pure. Cela signifie immédiatement que votre destructeur est utilisé dans votre programme. Ceci, à son tour, signifie que le définition de ce destructeur est nécessaire. Vous n'avez pas à donner une définition, de sorte que le compilateur a signalé une erreur.
Dans votre troisième exemple le destructeur est non-virtuel. Puisque vous n'êtes pas à l'aide de le destructeur dans votre programme, aucune définition n'est requis et le code se compile (voir 12.4 pour une description détaillée de ce qui constitue un utilisation d'un destructeur).
Dans votre deuxième exemple vous avez affaire à un caprice de la mise en œuvre, déclenchée par le fait que le constructeur n'est insérée. Depuis le destructeur est non-virtuelle pure, la définition est nécessaire. Cependant, votre compilateur n'a pas de détecter l'erreur, c'est pourquoi le code semble compiler avec succès. Vous pouvez creuser pour les raisons de ce comportement dans les détails de la mise en œuvre, mais à partir du C++ point de vue de cet exemple est aussi cassée que le premier, pour exactement la même raison.
La réponse à votre première question,
...est, tout simplement, que vous n'avez pas de définition pour le destructeur.
Maintenant votre deuxième question est un peu plus intéressant:
Et la raison en est que votre compilateur n'a pas besoin de
X
's destructeur puisque vous n'avez jamais instanciéeX
, il a jeté l'ensemble de votre classe à l'écart. Si vous essayez de compiler ce programme, vous obtiendrez un externe non résolu:Mais si vous commentez
X x;
, elle compile très bien, comme vous l'avez observé.Maintenant, nous allons revenir à pourquoi il ne compile pas si le destructeur si
virtual
. Je suis spéculer ici, mais je crois que la raison en est que, puisque vous avez un destructeur virtuel,X
est maintenant un polymorphe de la classe. Afin de lay-out polymorphes classes dans la mémoire, les compilateurs qui mettent en œuvre le polymorphisme à l'aide d'un vtable besoin d'adresses pour chaque fonction virtuelle. Vous n'avez pas mis en œuvreX::~X
, donc un externe non résolu résultats.Pourquoi ne pas le compilateur, il suffit de jeter
X
comme il l'a fait quandX
n'était pas un polymorphe de la classe? Plus de spéculation ici. Mais j'attends la raison en est que, même si vous n'avez pas directement instanciéX
, il ne peut pas être sûr que nulle part dans votre code ne unX
vivre, masqerading que quelque chose d'autre. Pour un exemple, considérons une classe de base abstraite. Dans ce cas, vous ne serez jamais à instancierBase
directement et le code pourDerived
peut être totalement séparée de l'unité de traduction. Ainsi, lorsque le compilateur arrive à cette polymorphe de la classe, il ne peut pas les jeter même si elle ne vous connaît pas instancié.Ce ne sont pas un programme complet (ou même complète de DLL). Quand vous obtenez l'erreur, vous êtes en fait aidé, parce que X est inutilisable sans une définition de ~X()
Tout cela signifie, c'est que le compilateur exemple besoin d'une définition dans certains cas. Même si il compile, il ne fait rien.
J'ai un soupçon sur ce qui c'est mise en œuvre, les comportements définis. Voici pourquoi
GCC donne une erreur comme ci-dessous, qui encore une fois, est très évocatrice (pour moi au moins) sur un non-standard détail de l'implémentation de la mise en œuvre des fonctions virtuelles
Je suis confus si un diagnostic qui est vraiment nécessaire, à partir d'un compilateur pour l'OP de code, donc la pensée de cet affichage, même que je risque de downvotes :). Bien sûr, un bon compilateur doit, je suppose.
Vous pouvez peut-être s'en sortir avec ce parce que les deux fabr et destr sont privé - si il n'y a pas d'autre ref de la classe X dans votre construction puis le compilateur peut-être en déduire que la destr n'est pas nécessaire, de sorte que le manque d'une définition n'est pas trop grave.
Cela ne veut pas m'expliquer pourquoi le cas 1 échoue tandis que 2 et 3 construire OK, bien. Me demande ce qui se passe si les deux sont-ils rendus publics?