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

choix entre dynamic_cast et une fonction virtuelle d'identification

7 réponses
Avatar
Marc G
Bonjour,
J'ai une hiérarchie de classes dérivées d'une même classe de base Base, pour
simplifier, quelque chose qui ressemble à ce qui suit :

class Base {
enum ID { ID1,ID2};
virtual ID id() { throw; }
};

class Derivee1 : public Base {
virtual ID id() { return ID1; }
};

class Derivee2 : public Base {
virtual ID id() { return ID2; }
};

Dans mon application, la performance est importante
=> je voudrais savoir quelle méthode est la + rapide pour "identifier" une
classe à partir d'un pointeur sur la classe de base

Base *ptr=new Derivee1 ;

choix 1 :
if (ptr->id()==ID1)
...
ou choix2
if (dynamic_cast<Derivee1*>(ptr))
...

A priori, les deux sont équivalentes (et la seconde présente l'avantage
d'éviter l'oubli de la redéfinition de méthode virtuelle...)
Est-ce bien le cas ?
Merci à vous.
Marc

7 réponses

Avatar
Michael DOUBEZ
Marc G a écrit :
J'ai une hiérarchie de classes dérivées d'une même classe de base Base,
pour simplifier, quelque chose qui ressemble à ce qui suit :

class Base {
enum ID { ID1,ID2};
virtual ID id() { throw; }
};

class Derivee1 : public Base {
virtual ID id() { return ID1; }
};

class Derivee2 : public Base {
virtual ID id() { return ID2; }
};

Dans mon application, la performance est importante
=> je voudrais savoir quelle méthode est la + rapide pour "identifier"
une classe à partir d'un pointeur sur la classe de base

Base *ptr=new Derivee1 ;

choix 1 :
if (ptr->id()==ID1)
...
ou choix2
if (dynamic_cast<Derivee1*>(ptr))
...

A priori, les deux sont équivalentes (et la seconde présente l'avantage
d'éviter l'oubli de la redéfinition de méthode virtuelle...)



Pour ne pas oublier de redéfinir la méthode virtuelle tu peux aussi la
rendre virtuelle pure.

Est-ce bien le cas ?



Je ne sais pas, AMA ça dépends de la qualité du compilateur.

--
Michael
Avatar
Marc G
> Pour ne pas oublier de redéfinir la méthode virtuelle tu peux aussi la
rendre virtuelle pure.


oui, c'est certainement ce qu'il faut faire...
Je viens d'essayer et l'appel de la méthode virtuelle semble nettement plus
rapide (environ 7 fois + sur BCB).
Je ne comprends pas trop pourquoi mais bon, puisque c'est comme ça...
En fait je connais le mécanisme d'implémentation des tables virtuelles mais
pas celui des dynamic_cast...
(peut-être qu'il s'appuie sur une fonction virtuelle d'identification
justement, d'où le temps + élevé ?)
Si quelqu'un en sait plus...
Marc
Avatar
James Kanze
On Jul 29, 9:41 pm, "Marc G" wrote:

J'ai une hiérarchie de classes dérivées d'une même classe de
base Base, pour simplifier, quelque chose qui ressemble à ce
qui suit :



class Base {
enum ID { ID1,ID2};
virtual ID id() { throw; }
};



class Derivee1 : public Base {
virtual ID id() { return ID1; }
};



class Derivee2 : public Base {
virtual ID id() { return ID2; }
};



Dans mon application, la performance est importante => je
voudrais savoir quelle méthode est la + rapide pour
"identifier" une classe à partir d'un pointeur sur la classe
de base



La meilleur façon, c'est de ne pas avoir besoin de les
identifier. En général, s'il faut savoir la classe dérivée à
partir d'un pointeur à la classe de base, il y a quelque chose
qui ne colle pas. (Il y a des exceptions, évidemment.)

Base *ptr=new Derivee1 ;

choix 1 :
if (ptr->id()==ID1)
...
ou choix2
if (dynamic_cast<Derivee1*>(ptr))
...



A priori, les deux sont équivalentes (et la seconde présente
l'avantage d'éviter l'oubli de la redéfinition de méthode
virtuelle...)



Tout dépend du compilateur, mais en général, le dynamic_cast est
plus lourd et plus cher que l'appel d'une fonction viruelle.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
Marc G
> La meilleur façon, c'est de ne pas avoir besoin de les
identifier. En général, s'il faut savoir la classe dérivée à
partir d'un pointeur à la classe de base, il y a quelque chose
qui ne colle pas. (Il y a des exceptions, évidemment.)



pour cette fois, je suis dans une exception.
le dynamic_cast peut avoir l'"avantage" d'être utilisable aux niveaux
intermédiaires...
par exemple, si je dérive une classe Derivee11 de Derivee1, il marche
toujours...(mais ça peut aussi poser pb)
=> ça dépend vraiment de ce qu'on veut faire avec....
j'imagine bien le dynamic_cast implémenté comme une fonction virtuelle qui
retourne la valeur de la fonction de la classe parent immédiat si le test
échoue
(donc on balaie du dernier niveau à la classe de base). Est-ce bien le cas
?...
Marc
Avatar
Michael Doubez
On 29 juil, 22:11, "Marc G" wrote:
> Pour ne pas oublier de redéfinir la méthode virtuelle tu peux aussi la
> rendre virtuelle pure.

oui, c'est certainement ce qu'il faut faire...
Je viens d'essayer et l'appel de la méthode virtuelle semble nettement plus
rapide (environ 7 fois + sur BCB).
Je ne comprends pas trop pourquoi mais bon, puisque c'est comme ça...



Ca depends peut être du niveau d'optimisation demandé et je ne sais
pas si il y a eu beaucoup d'effort sur l'optimisation du dynamic_cast<>
(), il est rarement utilisé et rarement pour de bonne raisons.

En fait je connais le mécanisme d'implémentation des tables virtuelle s mais
pas celui des dynamic_cast...
(peut-être qu'il s'appuie sur une fonction virtuelle d'identification
justement, d'où le temps + élevé ?)
Si quelqu'un en sait plus...



Il utilise les RTTI de l'objet pointé pour determiné si il peut être
casté. En pratique, il doit s'agir d'un lookup dans une table.
Je suppose qu'il serait possible pour le compilateur d'optimiser dans
des cas simples (pas d'héritage multiple) pour faire un minimum de
check mais je suppose qu'un mécanisme général est plutôt utilisé.

Concernant ton problème, si déjà ta classe de base a des enums qui
représentent des sous-classes, alors tu a déjà une dépendance
circulaire et je suppose que le nombre de classes est stable. Il
serait alors envisageable d'utiliser un pattern visitor; c'est ce qui
te donnerai le plus de flexibilité (au prix de la dépendance
circulaire mais tu l'a déjà).

--
Michael
Avatar
James Kanze
On Jul 30, 9:24 am, Michael Doubez wrote:
On 29 juil, 22:11, "Marc G" wrote:


[...]
> En fait je connais le mécanisme d'implémentation des tables
> virtuelles mais pas celui des dynamic_cast...
> (peut-être qu'il s'appuie sur une fonction virtuelle
> d'identification justement, d'où le temps + élevé ?) Si
> quelqu'un en sait plus...



Il utilise les RTTI de l'objet pointé pour determiné si il
peut être casté. En pratique, il doit s'agir d'un lookup dans
une table. Je suppose qu'il serait possible pour le
compilateur d'optimiser dans des cas simples (pas d'héritage
multiple) pour faire un minimum de check mais je suppose qu'un
mécanisme général est plutôt utilisé.



En général, le dynamic_cast est plus lent parce qu'il doit
considérer plus d'altérnatifs. Quand on appelle une fonction
virtuelle, il y a toujours exactement une seul résolution
possible, et il y a toujours une résolution. Tandis qu'on peut
faire un dynamic_cast à à peu près n'importe quoi, et il peut
échouer. Le résultat, c'est que quand on appelle une fonction
virtuelle, c'est en gros la lecture d'une adresse à une adresse
donnée, tandis que le dynamic_cast doit, effectivement faire une
recherche dans un tableau, en tenant compte de ce que ce qu'il
cherche ne s'y trouve peut-être pas.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
James Kanze
On Jul 29, 10:26 pm, "Marc G" wrote:
> La meilleur façon, c'est de ne pas avoir besoin de les
> identifier. En général, s'il faut savoir la classe dérivée à
> partir d'un pointeur à la classe de base, il y a quelque
> chose qui ne colle pas. (Il y a des exceptions, évidemment.)



pour cette fois, je suis dans une exception.
le dynamic_cast peut avoir l'"avantage" d'être utilisable aux
niveaux intermédiaires...
par exemple, si je dérive une classe Derivee11 de Derivee1, il
marche toujours...(mais ça peut aussi poser pb)
=> ça dépend vraiment de ce qu'on veut faire avec....



Je n'y ai rien compris, mais en général, quand il y a
dynamic_cast dans tous les sens, c'est qu'on n'a pas prévu ce
qu'il fallait dans la classe de base.

j'imagine bien le dynamic_cast implémenté comme une fonction
virtuelle qui retourne la valeur de la fonction de la classe
parent immédiat si le test échoue (donc on balaie du dernier
niveau à la classe de base). Est-ce bien le cas ?...



Dans les implémentations classique, le dynamic_cast renvoie
effectivement à un pointeur dans le vtbl. Un pointeur à des
données, en revanche, et non à une fonction (mais on pourrait
aussi l'implémenter avec une fonction différente par classe).
Ensuite, le code recherche la classe voulue dans ces données.

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