2010-06-15 4 views
2

J'ai une question sur le code suivant:C++ problème destructor avec boost :: scoped_ptr

#include <iostream> 
#include <boost/scoped_ptr.hpp> 

class Interface 
{ 
}; 

class A : public Interface 
{ 
    public: 
     A() { std::cout << "A()" << std::endl; } 
     virtual ~A() { std::cout << "~A()" << std::endl; } 
}; 


Interface* get_a() 
{ 
    A* a = new A; 
    return a; 
} 

int main() 
{ 
    { 
     std::cout << "1" << std::endl; 
     boost::scoped_ptr<Interface> x(get_a()); 
     std::cout << "2" << std::endl; 
    } 
    std::cout << "3" << std::endl; 
} 

Il crée la sortie suivante:

1 
A() 
2 
3 

Comme vous pouvez le voir, il ne remet pas le destructeur de A. La seule façon dont je vois que le destructeur de A est appelé est d'ajouter un destructeur pour la classe Interface comme ceci:

virtual ~Interface() { } 

Mais je veux vraiment éviter toute implémentation dans ma classe Interface et virtual ~Interface() = 0; ne fonctionne pas (produit des erreurs de l'éditeur de liens se plaignant d'une implémentation non existante de ~Interface(). Donc ma question est: Qu'est-ce que je dois changer pour que le destructeur soit appelé, mais (si possible) quitte l'interface en tant qu'interface (seulement des méthodes abstraites).

+0

Cette question recouvre essentiellement: http://stackoverflow.com/questions/270917/why -should-i-declare-a-virtual-destructor-for-an-abstract-class-in-c –

+2

Vous pouvez combiner '= 0' avec un corps vide pour éviter l'erreur, mais il y a peu d'avantages à le faire. C'est du C++, et il n'y a pas une "interface" - ce ne sont que des classes - donc il n'y a pas de bonne raison d'éviter les fonctions avec des corps. Surtout dans un cas idiomatique comme un destructeur virtuel. –

+0

J'ai supprimé les balises de pointeur intelligentes, car cela ne concerne pas vraiment les pointeurs intelligents, mais plutôt les destructeurs de classe de base. –

Répondre

5

Vous devez définir une destructor virtuelle dans la classe de base, sinon vous aurez pas le comportement polymorphique.

Et plus important encore, vous obtenez un comportement indéfini; §5.3.5/3:

Si le type statique de l'opérande est différent de son type dynamique, le type statique doit être une classe de base de type dynamique de l'opérande et le type statique doit avoir un destructeur virtuel ou le comportement est indéfini.

Emphasis mine.


Je dirais le meilleur est celui-ci:

class Interface 
{ 
public: 
    virtual ~Interface(void) = 0; 
}; 

inline Interface::~Interface(void) {} 

Le compilateur peut facilement inline ce, contrairement à une solution où la mise en œuvre réside dans un fichier source. (En parlant de cela, cette solution ne vous oblige même pas à en avoir une.) Elle laisse également la classe pure virtuelle.

+0

L'autre solution (avec une fonction en dehors du corps de la classe) ne nécessite pas de fichier source - vous pouvez le déclarer directement dans l'en-tête tant que c'est «inline». Il y a aussi une possibilité très hypothétique que le compilateur élimine complètement le vtable pour une classe donnée si toutes ses fonctions sont purement virtuelles, y compris destructor (il y a une différence potentiellement observable due à la commutation vtable pendant la construction/destruction). ça ne vaut pas la peine, mais j'ai travaillé avec une base de code où nous avions besoin de '__novtable' régulièrement une fois ... –

+0

@Pavel: Tu as raison. Donc c'est juste que je change de poste. : P – GManNickG

+0

Merci! Cela semble vraiment être la meilleure solution pour ce problème :) –

2

Vous devez déclarer le destructeur virtual si vous souhaitez supprimer des objets de classe dérivée via un pointeur vers votre type d'interface de classe de base, et que ce destructeur doit avoir une implémentation.

Vous pouvez toujours le déclarer pur virtuel, cependant:

class Interface 
{ 
public: 
    virtual ~Interface() = 0; 
}; 

inline Interface::~Interface() { } 
+3

Vous ne pouvez pas faire cela comme écrit - alors que les fonctions virtuelles pures peuvent avoir des corps, vous ne pouvez pas avoir une définition de fonction et '= 0' tout en un. Vous devez mettre une déclaration (sans corps) mais avec '= 0' dans le corps de la classe, et la définition réelle (avec un corps) à l'extérieur, correctement qualifiée. –

+0

Clever. J'apprends une autre nouvelle chose. –

+1

@Pavel: Merci ... J'aurais dû apprendre maintenant à ne pas utiliser Visual C++ pour vérifier la syntaxe :-) –

2

Vous devez définir une version virtuelle pure du destructeur d'interface, mais vous devez également définir le corps du destructeur. C'est une sorte de cas bizarre en C++ où même si la fonction est virtuelle, elle doit être définie car après l'appel du destructeur A, le destructeur d'instance sera également appelé.

Ainsi, la bonne réponse est:

virtual ~Interface() = 0; 

Et plus tard, dans un fichier cpp:

Interface::~Interface() {} 
+2

'Interface' est assez simple qu'il pourrait être bien de laisser ce destructeur en ligne. –