Nous allons pour cela reprendre notre bonne veille classe point :
class point { int x,y; public: void init(int,int); void deplace(int,int); void affiche(); };
Nous pourrions avoir envie de définir une classe pointcol qui serait dérivé de la classe point mais qui comporterai une données membre supplémentaire qui donnerai la couleur du point dans le plan :
class pointcol : public point { short col; public: void couleur(cl){col=cl;} };
Notez la déclaration :
class pointcol : public point
Elle spécifie que la classe pointcol est une classe dérivé de la classe de base point. De plus le mot clé public indique que les membres publiques de point seront des membres publiques de pointcol. Nous verrons plus loin qu'il existe plusieurs mode d'accé aux classes de bases par les classes dérivé.
Maintenant chaque objet de type pointcol peut :
- faire appel aux méthodes publiques de pointcol
- faire appel aux méthodes publiques de point
Comme nous l'avons précisé ceci n'etait qu'une approche de l'héritage, nous avons passé sur beaucoup de problème. En effet l'accès aux données publique de la classe de base est possible par la classe dérivée mais il n'en est pas pareil pour les données privées qui reste inaccessible directement par les fonctions membre de poincol, nous ne pourrions pas, par exemple, définir une fonction affichecol() qui afficherait les cordonnées du point et sa couleur car nous n'avons pas accès directement aux données membre de la classe de base. Par contre nous pourrions à l'intérieure d'une fonction de la classe dérivée, appeler une fonction de la classe de base pour résoudre ce problème :
void pointcol::affichecol() { affiche(); cout<<"je suis de couleur :"<<col<<endl; }
Ceci ne résout pas accès aux données membre mais il permet de contourner le problème.
Nous pourrions alors dans la même optique "surdéfinir" les fonctions de la classe de base dans la classe dérivé. Les fonctions affiche() de point et pointcol porteraient le même nom. Mais cela poserait un problème car au sein d'une fonction affiche() (de pointcol) nous ferions appel à la fonction affiche()(de point) le compilateur croira à l'appel de la même fonction, il y aura un appel récursif. Nous devons donc utiliser l'opérateur de résolution de porté'::' afin d'empêcher un tel comportement.
De manière comparable si pour un objet p de pointcol on fait l'appel p.affiche(), il s'agira de la fonction affiche() de pointcol. Pour avoir accé a la fonction affiche() de point il faudra faire l'appel p.point::affiche().
Voici comment nous pourrions adapté l'exemple précédent :
class pointcol : public point { short col; public: void couleur(cl){col=cl;} void affiche(); void init(int,int,short); }; void pointcol::affiche { point::affiche(); cout<<"je suis de couleur :"<<col<<endl; } void pointcol::init(int abs, int ord, short cl) { point::inti(abs,ord); col=cl; }
Si la classe point posséde un constructeur, alors il est possible de faire appel a se constructeur lors de la création d'un objet de la classe dérivé pointcol dans son constructeur :
class point { int x,y; public: point (int abs=0,int ord=0) :x(abs), y(ord) {} void deplace(int,int); void affiche(); }; class pointcol : public point { short col; public: pointcol(int abs=0,int ord=0,short cl=0) :point(abs,ord),col(cl) {} void affiche(); };
L'appel du constructeur de point se fait avant le constructeur de pointcol car il existe toujours un objet de type point dans un objet de type pointcol.
Nous avons vue tout à l'heure que nous avons pas accès aux données membre privé d'une classe de base par une classe dérivé. En fait la classe dérivé peut avoir un droit d'accès sur ces données membre. En effet il existe un autre type de protection : le mode protégé, mot clé protected.
Si le mode privé bloque totalement l'accès sur l'extérieure le mode protégé quand a lui bloque l'accès extérieure mais rend les données membre pubique pour les classe dérivées.
Bien sur nous pouvons restreindre encore cette accès, en effet nous pouvons aussi déclarer le mode d'héritage de la classe dérivé( class pointcol : public point) nous pouvons alors aussi changer ce mode d'accès aux classes de base.
voici le tableaux de correspondance des mode de protection :
Mode d'héritage |
Statut dans la classe de base |
Statut dans la classe dérivé |
private |
Private |
inaccesible |
private |
Protected |
private |
private |
Public |
private |
protected |
Private |
inaccesible |
protected |
Protected |
protected |
protected |
Public |
protected |
public |
Private |
inaccesible |
public |
Protected |
protected |
pubic |
Public |
public |
D'une manière général, en P.O.O., on considère qu'un objet d'une classe dérivé peut remplacer un objet de la classe de base : en effet si une fonction quelconque attend un objet de la classe point il peut recevoir un objet de la classe pointcol.
En effet tout membre se trouvant dans la classe de base se retrouve forcément dans la classe dérivé. De même toute action réalisable sur une classe de base sera réalisable sur une classe dérivé( se qui veut pas dire que le résultat sera satisfaisant).
Bien entendu les réciproques sont fausses, en effet on ne peut pas coloré un objet de type point.
En C++ cette compatibilité va s'appliquer avec une certaine nuance : elle ne s'applique que dans le cas de classe dérivé publiquement.