Une méthode virtuelle appelée depuis un destructeur n'est pas héritée!?

Résolu
cs_Forman Messages postés 600 Date d'inscription samedi 8 juin 2002 Statut Membre Dernière intervention 6 avril 2010 - 15 juin 2007 à 21:58
cs_Forman Messages postés 600 Date d'inscription samedi 8 juin 2002 Statut Membre Dernière intervention 6 avril 2010 - 16 juin 2007 à 10:52
Bonjour(soir) à tous,

je viens de me mettre au C++ et à GLUT et je bute sur un problème que je n'arrive pas à m'expliquer. D'abord, voilà le code en question (qui permettra de clarifier mes explications par la suite je l'espère):
template<class THANDLE=int,unsigned int INVALID_HANDLE=0> class Handled{
protected:
THANDLE Handle;

Handled(){
Handle=INVALID_HANDLE;
}

~Handled(){
if (handleAllocated())
deleteHandle();
}

void handleNeeded(){
if (!handleAllocated())
createHandle();
};

virtual void createHandle()=0;
virtual void deleteHandle()=0;
public:
int handleAllocated(){
return Handle!=INVALID_HANDLE;
};

THANDLE getHandle(){
handleNeeded();
return Handle;
};

void recreateHandle(){
if (handleAllocated()){
deleteHandle();
Handle=INVALID_HANDLE;
handleNeeded();
}
};
};

class Window: public Handled{
protected:
char *Name;
Window *Parent;
int X,Y,Width,Height;

void call(){
glutSetWindow(getHandle());
};

void createHandle();
void deleteHandle();
public:
Window(char *name,int x=100,int y=100,int width=320,int height=240):Name(name),Parent(NULL),X(x),Y(y),Width(width),Height(height){
handleNeeded();
};

Window(char *name,Window *parent,int x=80,int y=60,int width=160,int height=120):Name(NULL),Parent(parent),X(x),Y(y),Width(width),Height(height){
handleNeeded();
};
}

void Window::createHandle(){
/* Code qui crée une fenêtre avec GLUT et stocke l'index retourné dans Handle */
};

void Window::deleteHandle(){
/* Code qui crée une fenêtre avec GLUT et stocke l'index retourné dans Handle */
};

Donc voilà j'ai une classe de base qui sert à dériver des sous-classes encapsulant des objets systèmes auxquels on accède via une série d'API et un "Handle" qui est typiquement un entier. Le template avec THANDLE et INVALID_HANDLE sert à faire la distinction entre les objets systèmes qui sont liés à un entier signé (fenêtres GLUT par exemple) et ceux qui sont liés à un entier non signé (identifiants de texture OpenGl par exemple).

Les appels aux API servant à allouer/libérer les Handle systèmes en question sont définis dans la classes descendante en surchargeant les méthodes virtuelles pures createHandle et deleteHandle. Tout se passe bien dans les constructeurs qui appellent (directement ou indirectement) createHandle, la méthode descendante est appelée et mon objet système est bien créé.

Mais, et ça je n'arrive pas à l'expliquer, lorsqu'une instance de la classe descendante est détruite, ce n'est pas le cas : c'est la méthode de base deleteHandle qui est appelée... générant bien évidemment une exception "pure virtual function call". J'ai naïvement essayé de rendre le destructeur virtuel, voire de passer par un pointeur sur l'instance en espérant court-circuiter le typage à la compilation (car c'est de ça qu'il s'agit je suppose) rien n'y fait.

Est-ce une spécification du C++ (toute la doc que j'ai lue jusque-là n'en faisait pas mention en tout cas)? Est-ce que ça vient de ce que ma classe est une template?

Merci d'avance de votre aide.

3 réponses

cs_aardman Messages postés 1905 Date d'inscription mercredi 22 janvier 2003 Statut Membre Dernière intervention 17 septembre 2012 3
16 juin 2007 à 01:02
Salut,

Au moment de l'appel du destructeur de ta classe de base, le
destructeur de ta classe derivée à déja été appelé, et comme un appel
virtuel peut utiliser les données de ta classe derivée, il est interdit
ici.

Même raisonnement pour le constructeur, si tu appeles une fonction
virtuelle depuis le constructeur de ta classe de base, ca signifierait
qu'une methode de ta classe derivée peut etre appelée avant que le
constructeur de cette meme classe ai été appelé, ce qui est dangereux
puisque les données de ta classe derivée n'ont pas encore été
initialisées.
3
luhtor Messages postés 2023 Date d'inscription mardi 24 septembre 2002 Statut Membre Dernière intervention 28 juillet 2008 6
16 juin 2007 à 00:28
Window * pWindow = new Window();
Handled * pHandled = pWindow;
delete pHandled;

Si ton destructeur n'est pas virtuel, c'est le destructeur de pHandled qui sera appelé dans ce cas.

delete pWindow <= ici le destructeur de Window.

Donc ca coute rien de toujours mettre le destructeur virtuel:
virtual ~Handled();

Mais ya aucun pb. Sauf que toi il semble que tu l'ais mis virtuel pure, ce qui n'a évidemment aucun sens ici, mais c'est faisable dans certain cas.
0
cs_Forman Messages postés 600 Date d'inscription samedi 8 juin 2002 Statut Membre Dernière intervention 6 avril 2010 1
16 juin 2007 à 10:52
Bonjour,

Merci d'avoir pris la peine de répondre.

@luthor: en fait je n'ai guère besoin de faire un destructeur virtuel, sachant que ma classe Handled est seulement là pour m'éviter d'avoir à retaper 10 lignes à chaque nouvelle classe encapsulante. Mais j'y penserai, c'est plus prudent.

@aardman: merci, effectivement l'argument est imparable.
0
Rejoignez-nous