OVH Cloud OVH Cloud

Constructeur & fonctions virtuelles pures

3 réponses
Avatar
Mathieu Peyréga
Bonsoir,

à la fin du thread "Heritage" s'est posé le cas où une fonction
virtuelle pure est apellée depuis un constructeur :

struct A
{
inline A(void) { this->f(); }
inline virtual ~A(void) {}
virtual void f(void) = 0;
}

struct B : public A
{
inline void f(void) {}
}

int main(int argc, char *argv[])
{
B b;
return 0;
}

Ce code ne compile pas sous gcc 3.3.3 avec une erreur du genre appel de
fonction virtuelle pure. Sous visual studio 6.0 sp6 il compile mais ne
linke pas

En revanche :

struct A
{
inline A(void) { this->g(); }
inline virtual ~A(void) {}
inline void g(void) { this->f(); }
virtual void f(void) = 0;
}

struct B : public A
{
inline void f(void) {}
}

int main(int argc, char *argv[])
{
B b;
return 0;
}

compile linke et plante à l'exécution avec les 2 compilateurs
mentionnés, l'erreur étant du genre "pure virtual function called"
Je comprend à peu près ce qui se passe : la partie correspondant à "B"
n'est pas encore construite que le constructeur de "A" essaye d'appeller
une méthode virtuelle à laquelle il n'a pas encore accès...

mais ma question est :
que dis la norme ?, est-ce une non-conformité du compilo ? pourquoi le
compilo voit-il le problème au moment de la compilation lorsque l'appel
est direct depuis le compilateur et ne dis rien si on augmente d'un
niveau d'indirection ?

merci pour vos réponses éventuelles,

Mathieu

3 réponses

Avatar
Arnaud Debaene
Mathieu Peyréga wrote:
<snip>

mais ma question est :
que dis la norme ?,
Qu'on a pas le droit d'appeler une fonction virtuelle depuis un constructeur

ou un destructeur. Ou plus précisément, on peut l'appeler mais la liaison
est statique, c'est à dire que c'est la fonction définie dans la classe du
constructeur/destructeur en cours qui est appelée. Si ta fonction est
virtuelle pure, il n'y a bien sûr pas d'implémentation d'une telle fonction,
d'où ton plantage.

est-ce une non-conformité du compilo ?
Non.


pourquoi le
compilo voit-il le problème au moment de la compilation lorsque
l'appel est direct depuis le compilateur et ne dis rien si on
augmente d'un niveau d'indirection ?
Parce que le compilateur n'est pas assez malin pour suivre ton niveau

d'indirection et se rendre compte que tu appelles un méthode pure. Dans le
raisonnement du compilateur, g pourrait être appelée après la construction
de l'objet et est donc parfaitement valide en elle-même. Il ne vérifie pas
le fait que l'appel de g depuis le constructeur est invalide.
La norme ne dicte rien du tout concernant la clareté/pertinence des messages
d'erreurs du compilateur ;-)

Arnaud

Avatar
kanze
"Arnaud Debaene" wrote in message
news:<407b21e2$0$494$...
Mathieu Peyréga wrote:
<snip>

mais ma question est :
que dis la norme ?,


Qu'on a pas le droit d'appeler une fonction virtuelle depuis un
constructeur ou un destructeur. Ou plus précisément, on peut l'appeler
mais la liaison est statique, c'est à dire que c'est la fonction
définie dans la classe du constructeur/destructeur en cours qui est
appelée. Si ta fonction est virtuelle pure, il n'y a bien sûr pas
d'implémentation d'une telle fonction, d'où ton plantage.


C'est un peu plus complex que ça. La règle, c'est que l'appel dynamique
(virtuell) d'un fonction ne doit jamais se résoudre à une fonction
virtuelle pûre. Si c'est le cas (et ça ne peut être le cas que si la
fonction est appelée dans un constructeur ou un destructeur), le
programme a un comportement indéfini. Et ça, que la fonction soit
définie ou non. En fait, tous les compilateurs que j'ai jamais utilisé
termine le programme au moyen d'abort() (mais parfois sans autre message
d'erreur) si on essaie de l'appeler.

Si la fonction est définie, en revanche, tu peux bien l'appeler si tu
inhibes la résolution dynamique, au moyen de NomDeClasse::.

est-ce une non-conformité du compilo ?
Non.


pourquoi le compilo voit-il le problème au moment de la compilation
lorsque l'appel est direct depuis le compilateur et ne dis rien si on
augmente d'un niveau d'indirection ?


Parce que le compilateur n'est pas assez malin pour suivre ton niveau
d'indirection et se rendre compte que tu appelles un méthode pure.


C'est plus subtile que ça. Disons que le compilateur ne peut traiter le
code comme une erreur réele que s'il peut prouver qu'il serait executer.
Si la classe a trois constructeurs, par exemple, et il n'y a qu'un seul
qui appel une fonction virtuelle pûre, un programme qui n'utilise jamais
ce constructeur est légal. Un compilateur ne peut donc réfuser à
compiler le code que s'il peut prouver que le constructeur en question
sert réelement.

Dans le raisonnement du compilateur, g pourrait être appelée après la
construction de l'objet et est donc parfaitement valide en elle-même.
Il ne vérifie pas le fait que l'appel de g depuis le constructeur est
invalide. La norme ne dicte rien du tout concernant la
clareté/pertinence des messages d'erreurs du compilateur ;-)


C'est que l'analyse inter-fonctionnel est encore de l'exception dans les
compilateurs. Ce qui veut dire, en fait, qu'il ne peuvent pas réfuser à
compiler le code, parce qu'il faut de l'analyse inter-fonctionnel pour
savoir que le constructeur en question est réelement appelé.

Évidemment, si le compilateur voir un appel flagrant d'une fonction
virtuelle pûre dans un constructeur, il est libre à émettre un
avertissement, même sans savoir si le constructeur sert réelement.

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Mathieu Peyréga
Merci pour vos réponses !