OVH Cloud OVH Cloud

signification de public virtual CLASS_NAME

16 réponses
Avatar
Stephane Wirtel
Bonsoir,

Je me souviens que lors de mon examen d'embauche, j'avais eu une question
sur un bout de code, avec le temps et après avoir regardé le livre de
Bjarne, je n'ai pas encore vu le bout de code en question dans une
littérature.

class Person {
public:
Person (void);
~Person (void);
};

class Employe : public virtual Person {
public:
Employe (void);
~Employe (void);
};

Que signifie le "public virtual Person" ?
1) Je rend le classe Person virtuelle lors de l'héritage ?
2) Je ne sais vraiment pas.

Stef
--
Stephane Wirtel <stephane.wirtel@belgacom.net>

10 réponses

1 2
Avatar
Samuel Krempp
(05 May 2005 01:18,

Bonsoir,

Je me souviens que lors de mon examen d'embauche, j'avais eu une question
sur un bout de code, avec le temps et après avoir regardé le livre de
Bjarne, je n'ai pas encore vu le bout de code en question dans une
littérature.


ah pourtant ça doit être qque part dans le 'TCPL'. il y a forcément un mot
sur l'héritage multiple, les 'diagrammes en losange', et donc fatalement
sur l'héritage virtuel.

class Person {
public:
Person (void);
~Person (void);
};

class Employe : public virtual Person {
public:
Employe (void);
~Employe (void);
};

Que signifie le "public virtual Person" ?
1) Je rend le classe Person virtuelle lors de l'héritage ?
2) Je ne sais vraiment pas.


-> 2.
Je crois qu'on dit que Person est une base 'virtuelle' de Employe.

à ce stade ça ne change pas la sémantique du tout, mais si on imagine une
classe Employe2 identique, et une classe :

class Truc : public Employe, Employe2 {
}

alors, le fait que la dérivation est virtuelle a un effet : la classe Truc
n'aura qu'une seule base 'Person', au lieu de 2 (une du coté Employe -
Employe::Person, et une du coté Employe2 : Employe2::Person) sans le
mot-clef virtual.

par contre, je n'ai pas d'exemple 'réaliste' en tête qui justifierait le
besoin concret de ce mécanisme plutot que de s'organiser autrement.

--
Sam

Avatar
Fabien LE LEZ
On Thu, 05 May 2005 03:51:04 +0200, Samuel Krempp
:

par contre, je n'ai pas d'exemple 'réaliste' en tête qui justifierait le
besoin concret de ce mécanisme plutot que de s'organiser autrement.


Le besoin est assez fréquent dans les hiérarchies de classes gérant
une interface graphique (GUI).

Classe de base : "Fenetre"

Une classe dérivée est "Bouton" : une fenêtre particulière qui réagit
au clic de souris en lançant une commande.

On crée souvent des types particuliers de fenêtres, par exemple, une
fenêtre avec un fond de couleur, ou une fenêtre dont le texte est
traduit automatiquement, etc. Il s'agit là encore de classes dérivées
de "Fenetre".

Si maintenant on veut créer une classe "bouton avec fond de couleur",
on se retrouve à dériver de Bouton _et_ de
"Fenetre_avec_fond_de_couleur".
Et comme un objet de cette classe correspond à une seule fenêtre à
l'écran, la plupart des membres doivent être uniques -- d'où
l'héritage virtuel.

Parfois, pour une raison ou une autre, ce n'est pas possible, ou trop
compliqué, à mettre en place. Une solution alternative est d'utiliser
les templates :

template <classe Machin> Machin_avec_fond_de_couleur
: public Machin
{...};

typedef Machin_avec_fond_de_couleur<Fenetre>
Fenetre_avec_fond_de_couleur;
typedef Machin_avec_fond_de_couleur<Bouton>
Bouton_avec_fond_de_couleur;

--
Le grand site de la philosophie animale : <http://perso.edulang.com/philo/>

Avatar
Stephane Wirtel
Merci pour les explications.

Je comprends mieux, et je vois l'intérêt surtout dans le cas de l'héritage
multiple qui peut engendrer de sérieux problèmes.

Stéphane
Avatar
Fabien LE LEZ
On Thu, 05 May 2005 04:19:26 +0200, Stephane Wirtel
:

Je comprends mieux, et je vois l'intérêt surtout dans le cas de l'héritage
multiple qui peut engendrer de sérieux problèmes.


C'est pas pour rien que Sun a éludé le problème...

--
Le grand site de la philosophie animale : <http://perso.edulang.com/philo/>

Avatar
James Kanze
Fabien LE LEZ wrote:
On Thu, 05 May 2005 03:51:04 +0200, Samuel Krempp
:


par contre, je n'ai pas d'exemple 'réaliste' en tête qui
justifierait le besoin concret de ce mécanisme plutot que de
s'organiser autrement.



Le besoin est assez fréquent dans les hiérarchies de classes
gérant une interface graphique (GUI).


Classe de base : "Fenetre"


Une classe dérivée est "Bouton" : une fenêtre particulière qui
réagit au clic de souris en lançant une commande.


On crée souvent des types particuliers de fenêtres, par
exemple, une fenêtre avec un fond de couleur, ou une fenêtre
dont le texte est traduit automatiquement, etc. Il s'agit là
encore de classes dérivées de "Fenetre".


En général, la couleur du fond est une attribute de la classe.
On ne va pas en faire une classe exprès pour ça.

Si maintenant on veut créer une classe "bouton avec fond de
couleur", on se retrouve à dériver de Bouton _et_ de
"Fenetre_avec_fond_de_couleur".


Et comme un objet de cette classe correspond à une seule
fenêtre à l'écran, la plupart des membres doivent être uniques
-- d'où l'héritage virtuel.


En fait, je crois que l'exemple est assez mal choisi. Si on
conçoit de zéro, l'héritage dans les classes qui s'affichent est
linéaire. La seule fois que j'ai vu où il aurait fallu
l'héritage multiple, c'était dans Java Swing -- c'est une
solution naturelle quand on base une nouvelle solution sur une
ancienne, et qu'on veut que les deux fonctionnent en semble.
(Seulement, évidemment, si l'ancienne ne s'est pas servi de
l'héritage virtuelle, on est coincé.)

L'exemple du monde de la GUI, c'est plutôt des classes
gestionnaires d'évenemments. Typiquement, il existe bien des
interfaces (classes abstraites) du genre MouseEventHandler,
KeyboardEventHandler, etc. Alors, si ta classe doit gérer à la
fois des évenemments du clavier et de la souris... En revanche,
ce n'est pas sûr que l'héritage virtuel soit nécessaire dans ce
cas-là. Normalement, l'héritage ne doit pas aller très loin.
Si la classe que tu dérives n'est pas, elle, conçue comme une
classe de base, ce n'est pas la peine que l'héritage soit
virtuel. Si elle l'est en revanche (du genre
javax.swing.AbstractAction dans Swing), il vaut mieux. Au cas
où.

Il y a aussi des cas de mixin. Trouver un bon exemple n'est pas
facile, mais je sais que je m'en suis servi deux ou trois fois,
où il semblait la solution qui convenait le mieux.

Note bien qu'on peut toujours contourner le problème au moyen de
la délégation. Donc, par exemple, au lieu de dériver directement
de MouseEventHandler et de KeyboardEventHandler, on crée deux
petites classes qui en dérivent chacune d'une, qui contient un
pointeur à ma classe, et qui renvoie les évenements. Si le cas
arrive souvent, on pourrait même en créer un template pour ces
petites classes. Mais l'héritage multiple donne une solution
bien plus simple.

À cet égard, c'est intéressant de considérer le Java. Les
auteurs en ont écarté l'héritage multiple, dans le cas général,
en le disant trop compliqué. Mais vu qu'il est clairement la
solution préférée dans le cas de la gestion des évenemments (qui
ne se présente pas que dans les GUI), ils ont ajouté une
deuxième type de classe, appelé interface, avec des restrictions
draconniennes sur ce qu'on peut en faire (parce que sinon,
comment justifier deux définitions au niveau du langage pour un
seul concepte logique), mais qui permet l'héritage multiple, Or,
si tu étudies un peu la spécification de Java, tu t'apercevras
que quand on hérite d'une classe, c'est comme l'héritage normal
en C++, mais quand on hérite d'une interface, c'est plutôt comme
l'héritage virtuel en C++. Sauf, évidemment, on ne peut pas
créer un AbstractMouseEventHandler et
AbstractKeyboardEventHandler, avec des comportements par défaut
pour les évenemments qui nous intéressent pas, et puis en hérite
des deux. C'est un contraint que le C++ n'a pas. (L'idée qu'il y
a deux types de classes, dont l'héritage d'une est virtuelle par
défaut, et l'héritage de l'autre non, est peut-être
intéressante. Mais il ne faut pas que l'expressivité d'une en
soit émasculée.)

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


Avatar
James Kanze
Fabien LE LEZ wrote:
On Thu, 05 May 2005 04:19:26 +0200, Stephane Wirtel
:


Je comprends mieux, et je vois l'intérêt surtout dans le cas
de l'héritage multiple qui peut engendrer de sérieux
problèmes.



C'est pas pour rien que Sun a éludé le problème...


Par « Sun », je suppose que tu veux dire « Java ». Alors, pas
vraiment. Le problème est toujours là -- quand il te faut de
l'héritage multiple. Seulement, en Java, si les classes de base
ne sont pas des « interface » (et c'est extrèmement limité, ce
qu'on peut faire dans une interface), le problème est chez
l'utilisateur ; en C++, c'est au fournisseur du compilateur de
se débrouiller pour que ça marche, or qu'en Java, c'est 100% ton
problème.

Comme tout outil puissant, on peut se blesser en s'en abusant.
Mais quand il le faut, c'est bien agréable que ce soit les
auteurs du compilateur qui ont dû s'en occuper, et non moi.

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


Avatar
Samuel Krempp
(05 May 2005 11:30, <4279e78e$0$27953$) a
Normalement, l'héritage ne doit pas aller très loin.
Si la classe que tu dérives n'est pas, elle, conçue comme une
classe de base, ce n'est pas la peine que l'héritage soit
virtuel.


comment ça ?

Il y a aussi des cas de mixin. Trouver un bon exemple n'est pas
facile, mais je sais que je m'en suis servi deux ou trois fois,
où il semblait la solution qui convenait le mieux.


que veux-tu dire par 'cas de mixin' ?

Y-a-t-il des bons exemples de cas où l'on veut obtenir plusieurs fois une
même base par des chemins différents (bref où l'on veut précisément pas
d'héritage virtuel) ?

Sauf, évidemment, on ne peut pas
créer un AbstractMouseEventHandler et
AbstractKeyboardEventHandler, avec des comportements par défaut
pour les évenemments qui nous intéressent pas, et puis en hérite
des deux.


ces classes seraient utiles si l'on est quasi-toujours interessé par les
mêmes évenements (et qu'on veut éviter de taper les 3 ou 4 autres méthodes
vides), mais à priori on peut facilement s'en passer.
Je suis bien d'accord que la contrainte de Java sur les interfaces est une
limitation, mais dans ce cas là ça me semble pas trop gènant.

--
Sam

Avatar
James Kanze
Samuel Krempp wrote:
(05 May 2005 11:30,
<4279e78e$0$27953$) a


Normalement, l'héritage ne doit pas aller très loin. Si la
classe que tu dérives n'est pas, elle, conçue comme une classe
de base, ce n'est pas la peine que l'héritage soit virtuel.



comment ça ?


Si la classe qui dérive d'une interface n'est pas, elle, conçue
pour être une classe de base, il n'y a pas besoin qu'elle hérite
virtuellement de l'interface. Et typiquement, les héritages des
« interfaces » n'est pas très profond.

Il y a aussi des cas de mixin. Trouver un bon exemple n'est
pas facile, mais je sais que je m'en suis servi deux ou trois
fois, où il semblait la solution qui convenait le mieux.



que veux-tu dire par 'cas de mixin' ?


Y-a-t-il des bons exemples de cas où l'on veut obtenir
plusieurs fois une même base par des chemins différents (bref
où l'on veut précisément pas d'héritage virtuel) ?


Un mixin est une classe (souvent dérivée d'une base
« interface ») qui fournit une implémentation spécifique pour
une partie de l'interface. Dans un cas que j'ai eu, par exemple,
un « Termination Point » pouvait être en entrée, en sortie ou
bidirectionnel, et aussi sécouru ou non. J'ai donc défini des
implémentations TPIn, TPOut, TPBidir, et des implémentations
TPwBackup et TPwoBackup. Tous qui héritait de l'interface TP.
Pour créer un TP concret qui est sécouru et en entrée, on créait
une classe qui héritait de TPIn et de TPwBackup, sans une ligne
d'implémentation en plus.

C'est un exemple de l'héritage multiple d'implémentation.

On *pourrait* l'utiliser souvent. Dans la pratique, il y a
souvent de meilleurs solutions (AMA, en tout cas), mais de temps
en temps, il se présente un cas où c'est la solution idéale.

Sauf, évidemment, on ne peut pas créer un
AbstractMouseEventHandler et AbstractKeyboardEventHandler,
avec des comportements par défaut pour les évenemments qui
nous intéressent pas, et puis en hérite des deux.



ces classes seraient utiles si l'on est quasi-toujours
interessé par les mêmes évenements (et qu'on veut éviter de
taper les 3 ou 4 autres méthodes vides), mais à priori on peut
facilement s'en passer. Je suis bien d'accord que la
contrainte de Java sur les interfaces est une limitation, mais
dans ce cas là ça me semble pas trop gènant.


D'après mes expériences avec Java, c'est plutôt l'exception
d'être intéressé par plus d'un type d'évenemment pour chaque
source. Et que la solution la plus fréquente consistait à
implémenter deux classes, une dérivée de
AbstractMouseEventHandler, et l'autre de
AbstractKeyboardEventHandler. Avec les inner class de Java, ce
n'est pas trop de boulot, mais il mène bien à une prolifération
des petits objets supplémentaires. (En Java, où même un
BigDecimal s'alloue sur le tas, on ne le remarque pas, mais ça
va un peu à l'encontre à ce dont on a l'habitude en C++.)

De toute façon, il y a toujours des moyens à éviter l'héritage
virtuel. À prix de faire le boulot soi-même, plutôt que de le
faire faire par le compilateur, parfois.

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


Avatar
Fabien LE LEZ
On Thu, 05 May 2005 11:34:39 +0200, James Kanze :

C'est pas pour rien que Sun a éludé le problème...


Par « Sun », je suppose que tu veux dire « Java ».


Oui.

Alors, pas
vraiment. Le problème est toujours là


Oui. C'est exactement ce que j'ai dit : Sun a éludé[*] le problème.
C'est-à-dire, ils ont esquivé, fait comme si le problème n'existait
pas.


[*] (C'est le même mot en anglais : to elude)
--
Le grand site de la philosophie animale : <http://perso.edulang.com/philo/>


Avatar
Fabien LE LEZ
On Thu, 05 May 2005 11:30:08 +0200, James Kanze :

En général, la couleur du fond est une attribute de la classe.
On ne va pas en faire une classe exprès pour ça.


En général, oui. Mais si on veut quelque chose de non prévu par le
concepteur de la classe (afficher un bitmap en fond à la place d'une
couleur unie, par exemple), il faut bien dériver. C'est d'ailleurs
tout l'intérêt de la dérivation dans ces hiérarchies de classes :
faire quelque chose que l'auteur de la hiérarchie n'avait pas prévu.

Et quand on veut rajouter cette fonctionnalité dans plus d'une classe
de la hiérarchie, on a le choix entre l'héritage multiple ou les
templates.

Tiens, au fait, ça existe dans d'autres langages que C++, les
templates ?

--
Le grand site de la philosophie animale : <http://perso.edulang.com/philo/>

1 2