Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Destructeur de classe

3 réponses
Avatar
JKB
Bonjour à tous,

Je viens de rencontrer un problème bizarre et j'aimerais savoir si
c'est moi qui n'aie pas tout compris aux classes C++ ou si c'est mon
compilo qui a un problème.

Considérons une classe gérant un port quelconque (dans mon programme
un port série mais ce n'est pas important) dans laquelle est définie
une fonction purement virtuelle définie dans une classe héritée de
la première.

Le constructeur de la classe mère lance deux threads utilisant ma
fonction purement virtuelle :
- un pour la lecture sur ce port ;
- un second pour l'écriture sur ce port.

Là où ça se corse, c'est que mon constructeur est appelé depuis une
autre classe créant un thread.

Avec un petit dessin, cela donne :

main->classe driver
+-> thread spécifique créé dans le constructeur de driver
+-> constructeur serialPort
+-> thread lecture
+-> thread écriture

Le souci arrive lors de l'appel des destructeurs. Je détruis driver
(avec un delete, l'objet étant créé par un new) qui doit, il me
semble, appeler dans l'ordre inverse de création les destructeurs
associés. Ça se passe bien, sauf que j'ai un énorme problème avec ma
fonction virtuelle.

Mon destructeur fautif (celui de la classe implantant cette fameuse
fonction virtuelle) arrête le thread qui utilise cette fonction et
attend la fin de ce thread (avec un pthread_join()). L'ennui est
qu'à la fin du thread en question, j'utilise la fonction virtuelle
en question et qu'elle pointe déjà sur NULL. En pseudo-code, ça
donne :

~classe()
{
_requete_arret = -1;
pthread_kill(thread, SIGUSR1);
pthread_join(thread, NULL);
}

fonction()
{
while(_requete_arret == 0)
{
lecture_port();
fonction_virtuelle(); // vaut NULL dès l'appel à ~classe
}
}

Il m'aurait semblé logique que la fonction virtuelle soit
déréférencée en sortie du destructeur et non à l'entrée. Alors,
est-ce moi qui aie encore compris un truc de travers ?

Merci de vos lumières,

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr

3 réponses

Avatar
Pascal J. Bourguignon
JKB writes:

Mon destructeur fautif (celui de la classe implantant cette fameuse
fonction virtuelle) arrête le thread qui utilise cette fonction et
attend la fin de ce thread (avec un pthread_join()). L'ennui est
qu'à la fin du thread en question, j'utilise la fonction virtuelle
en question et qu'elle pointe déjà sur NULL. En pseudo-code, ça
donne :

~classe()
{
_requete_arret = -1;
pthread_kill(thread, SIGUSR1);
pthread_join(thread, NULL);
}

fonction()
{
while(_requete_arret == 0)
{
lecture_port();
fonction_virtuelle(); // vaut NULL dès l'appel à ~classe
}
}

Il m'aurait semblé logique que la fonction virtuelle soit
déréférencée en sortie du destructeur et non à l'entrée. Alors,
est-ce moi qui aie encore compris un truc de travers ?



Si je me souviens bien, en effet, il ne faut pas appeler de fonctions
virtuelles sur l'objet qu'on est en train de supprimer dans le
destructeur.

D'autre part, tu as là clairement un problème de synchronisation entre
threads, car la simple variable _requete_arret ne peut pas empêcher
l'appel à lecture_port et fonction_virtuelle (_requete_arret peut être
mis à -1 après le test du while). Il faut utiliser ici un mutex, afin
que le destructeur ne tue pas le thread, tant qu'il est encore dans la
boucle while.




--
__Pascal Bourguignon__ http://www.informatimago.com/
A bad day in () is better than a good day in {}.
Avatar
JKB
Le Fri, 28 Oct 2011 15:48:53 +0200,
Pascal J. Bourguignon écrivait :
JKB writes:

Mon destructeur fautif (celui de la classe implantant cette fameuse
fonction virtuelle) arrête le thread qui utilise cette fonction et
attend la fin de ce thread (avec un pthread_join()). L'ennui est
qu'à la fin du thread en question, j'utilise la fonction virtuelle
en question et qu'elle pointe déjà sur NULL. En pseudo-code, ça
donne :

~classe()
{
_requete_arret = -1;
pthread_kill(thread, SIGUSR1);
pthread_join(thread, NULL);
}

fonction()
{
while(_requete_arret == 0)
{
lecture_port();
fonction_virtuelle(); // vaut NULL dès l'appel à ~classe
}
}

Il m'aurait semblé logique que la fonction virtuelle soit
déréférencée en sortie du destructeur et non à l'entrée. Alors,
est-ce moi qui aie encore compris un truc de travers ?



Si je me souviens bien, en effet, il ne faut pas appeler de fonctions
virtuelles sur l'objet qu'on est en train de supprimer dans le
destructeur.

D'autre part, tu as là clairement un problème de synchronisation entre
threads, car la simple variable _requete_arret ne peut pas empêcher
l'appel à lecture_port et fonction_virtuelle (_requete_arret peut être
mis à -1 après le test du while). Il faut utiliser ici un mutex, afin
que le destructeur ne tue pas le thread, tant qu'il est encore dans la
boucle while.



C'est du pseudo-code. Le mécanisme est un peu plus compliqué et
prend naturellement en compte ce que tu m'indiques. C'est pour cela
que le pthread_kill() utilise un SIGUSR1. Dans mon code réel, cela
force le passage par le while().

Merci pour la réponse.

Cordialement,

JKB

--
Si votre demande me parvient sur carte perforée, je titiouaillerai très
volontiers une réponse...
=> http://grincheux.de-charybde-en-scylla.fr
Avatar
Fabien LE LEZ
On Fri, 28 Oct 2011 13:34:20 +0000 (UTC), JKB
:

Il m'aurait semblé logique que la fonction virtuelle soit
déréférencée en sortie du destructeur et non à l'entrée.



Je ne sais pas trop ce que tu appelles "déréférencée".

Dans un constructeur et un destructeur, le type dynamique est le même
que le type statique.

En fait, quand le destructeur de la classe de base est appelé, l'objet
n'a déjà plus la classe "dérivée". De même, quand le constructeur de
la classe de base est appelée, l'objet n'a pas encore la classe
"dérivée".