Cours C++.livre(Héritage)
28 pages
Français

Cours C++.livre(Héritage)

-

Le téléchargement nécessite un accès à la bibliothèque YouScribe
Tout savoir sur nos offres
28 pages
Français
Le téléchargement nécessite un accès à la bibliothèque YouScribe
Tout savoir sur nos offres

Description

CHAPITRE 14 HéritageLe langage C++ 217einev Télécommunications mjn 14.1 MotivationL'héritage est l'un des aspects les plus importants de C++, après l'encapsulation des don-nées (information hiding). L'encapsulation permet de protéger les données contre des accès etdes modifications incontrôlées; l'héritage permet la réutilisation de code à partir d'un type pré-cédemment défini.La réutilisation de code a bien sûr de tous temps été possible : qui n'a pas repris un mo-dule de code source (écrit par lui-même ou par un collègue), changé quelques lignes de code,et généré ainsi un nouveau module de code à très peu de frais ?Cette méthode, pour efficace et éprouvée qu'elle soit, a néanmoins beaucoup d'inconvé-nients. Enumérons-en quelques-uns, sans prétendre être exhaustifs par ailleurs:- Une erreur découverte plus tard dans l'un des deux segments de code a detrès fortes chances de se retrouver également dans l'autre. Il n'est en revanche pasévident que cette erreur sera corrigée dans les deux modules, surtout si le moduleoriginal a été développé par un auteur différent de celui ayant réalisé le modulecopié.- Dans le même contexte, une modification due par exemple à des nécessitésd'optimisation risquent de ne se retrouver que dans un des deux segments de code.Cet inconvénient rejoint le précédent dans le sens où le volume de code à gérer adoublé, rendant la tâche de gestion d'autant plus complexe et sujette à erreurs.- Une application peut avoir besoin des ...

Sujets

Informations

Publié par
Nombre de lectures 64
Langue Français

Extrait

CHAPITRE 14Le langag
Héritage
e C++
217
einev
14.1Motivation
Télécommunications
mjn
L'héritage est l'un des aspects les plus importants de C++, après l'encapsulation des don-nées (information hiding). L'encapsulation permet de protéger les données contre des accès et des modifications incontrôlées; l'héritage permet la réutilisation de code à partir d'un type pré-cédemment défini. La réutilisation de code a bien sûr de tous temps été possible : qui n'a pas repris un mo-dule de code source (écrit par lui-même ou par un collègue), changé quelques lignes de code, et généré ainsi un nouveau module de code à très peu de frais ? Cette méthode, pour efficace et éprouvée qu'elle soit, a néanmoins beaucoup d'inconvé-nients. Enumérons-en quelques-uns, sans prétendre être exhaustifs par ailleurs: - Une erreur découverte plus tard dans l'un des deux segments de code a de très fortes chances de se retrouver également dans l'autre. Il n'est en revanche pas évident que cette erreur sera corrigée dans les deux modules, surtout si le module original a été développé par un auteur différent de celui ayant réalisé le module copié. - Dans le même contexte, une modification due par exemple à des nécessités d'optimisation risquent de ne se retrouver que dans un des deux segments de code. Cet inconvénient rejoint le précédent dans le sens où le volume de code à gérer a doublé, rendant la tâche de gestion d'autant plus complexe et sujette à erreurs. - Une application peut avoir besoin des deux fonctionnalités, auquel cas elle doit importer les deux fragments de code. Si cet inconvénient peut paraître secon-daire eu égard aux ordinateurs modernes et à la mémoire virtuelle, il n'en reste pas moins que le code à charger est plus grand, d'où un temps de démarrage de l'ap-plication plus conséquent. De plus, il peut y avoir des conflits de nom, si la copie n'a pas été faite soigneusement. - Un fournisseur de logiciel désirant mettre à disposition du code (contre es-pèces, bien sûr) répugne à livrer du source, et préfère de loin livrer du code objet, dont il conserve le contrôle. Le code objet ne peut normalement que malaisément être adapté aux besoins spécifiques d’un client particulier; l’héritage offre une possibilité intéressante dans ce sens, du fait qu’il permet de modifier des détails d’un code objet tout en conservant le comportement de base d’un objet. Par contraste, l'héritage permet en principe de ne définir que les portions de code qui changent entre le segment de code original et le segment de code résultant. Le principe en est que l'on peut, à partir d'une classe de base, spécifier des classes dérivées dont le comportement est le même que celui de la classe de base, à certains détails près. La classe dérivée contient la classe de base comme un sous-ensemble.
218
Le langage C++
einev Télécommunications
14.2Implémentation
mjn
La classe de base implémente en principe des caractéristiques générales, qui sont aussi des caractéristiques des classes dérivées, alors que les classes dérivées se contentent de défi-nir des spécialisations de la classe de base.
FIGURE 3.Représentation de classes dérivées
Classe de Base
Classe dérivée Classe dérivée
Dans une relation d’héritage, la classe dérivée n’a pas accès aux membres privés de la classe de base. Par contre, elle a bien sûr accès, comme tout le monde aux membres publics. De plus, elle a la possibilité d’accéder aux membres protégésde la classe de base. Ceci est vrai pour tous les types de dérivation.
Le langage C++
219
einev
14.3Héritage publique
Télécommunications
mjn
L’héritage dit publique est la forme la plus importante d’héritage en C++. La syntaxe générale en est la suivante : class A { private : ... protected : ... public : ... }; class B : public A { private : ... public : ... }; B est la classe dérivée, alors que A est la classe de base pour B. Pour l’utilisateur de B, les membres de Aconservent leur visibilité. Les membres publics de A sont donc également des membres publics de B, les membres protégés de A sont donc également des membres pro-tégés de B.De manière générale, les membres de la classe de base conservent la même visibilité lorsque cette classe est dérivée de façon publique. L’héritage public est applicable chaque fois que l’on peut dire qu’un objet de la classe dérivéeest unobjet de la classe de base. Ainsi : class Bird { // Description d’un oiseau }; class Eagle : public Bird { // Description spécifique d’un aigle };
Un aigleest unoiseau, donc l’héritage public est de mise. Tout ce qui s’applique à un oiseau est également applicable à un aigle.Mais pas le contraire! L’aigle définit des spéci-fictés qui ne sont applicables qu’à lui-même. Ainsi, l’aigle est un rapace, ce qui ne veut pas dire que tous les oiseaux sont des rapaces !
220
Le langage C++
einev
Télécommunications
mjn
opLuu,hb éleiriqtfuaegem epnuitbs,ld iec”  ecAsot érurqeunsi”pv .oaDnuidtBuunre qaieo clà d ri euq etaler enu à  snessdete Butynv”irédpe  A.ies a”, n rança  
Beaucoup d'étudiants ne retiennent de leurs cours d'histoire que la date de la bataille de Marignan en 1515, et rien d'autre.S'il est une chose de ce cours à retenir concernant l'hé-ritage, c'est que l'héritage public correspond à une relation de type isa.
14.3.1exEelpm Nous allons établir une petite hiérarchie d’animaux (sans prétentions quelconques) pour illustrer le mécanisme de l’héritage. La figure suivante (Figure4, page225) illustre gra-phiquement ce que nous allons implémenter par un programme. Il y a bien sûr plusieurs manières d’implémenter une telle hiérarchie, et l’implémenta-tion d’une hiérarchie réelle à des fins de classification zoologique serait extrêmement com-plexe, comparativement à notre exemple.
#include <iostream.h> #include <string.h> class Animal { private : char * gifImageFile; char * animalName; // strdup est implémenté en standard sous UNIX. // avec les environnements de Microsoft (Visual C++) // Borland ou Symantec, il est nécessaire de redéfinir // cette fonction. char* strdup(char *c) { if (c == NULL) return NULL; char *s = new char [strlen(c) + 1]; strcpy(s, c); return s; } public : Animal(char *img = NULL, char *name = NULL) { gifImageFile = strdup(img); animalName = strdup(name); }  virtual ~Animal() { delete [] gifImageFile; delete [] animalName; } virtual void eat() = 0; virtual void sleep() = 0;
Le langage C++
221
einev
Télécommunications
virtual void identify(ostream& os) { os<<"Animal = "<<animalName; } }; // Mammifères : class Mammal : public Animal { public : Mammal(char *im, char *nm = "Mammal") : Animal(im, nm) {;} virtual ~Mammal() {;} virtual void identify(ostream& os) { os<<"Mammifere : "; Animal::identify(os); } }; // Oiseaux : class Bird : public Animal { public : Bird(char *im, char *nm = "Bird") : Animal(im, nm) {;} virtual ~Bird() {;} virtual void identify(ostream& os) { os<<"Oiseau : "; Animal::identify(os); } }; // Un rongeur est un mammifère : class Rodent : public Mammal { public : Rodent(char *im, char *nm = "Rodent") : Mammal(im, nm) {;} virtual ~Rodent() {;} virtual void identify(ostream& os) { os<<"Rodent : "; Mammal::identify(os); } }; // Une souris est un rongeur : class Mouse : public Rodent { public : Mouse(char *im = "AMOUSE.GIF", char *nm = "Mouse") : Rodent(im, nm) {;} virtual ~Mouse() {;} virtual void eat() { cout<<"I eat cheese ! "<<endl; } virtual void sleep() { cout<<"ron...ron"<<endl; } virtual void identify(ostream& os) { os<<"Souris : "; Rodent::identify(os); } }; class Feline : public Mammal { public : Feline(char *im, char *nm = "Felin") : Mammal(im, nm) {;} virtual ~Feline() {;} virtual void identify(ostream& os) { os<<"Felin : "; Mammal::identify(os); } }; 222
Le langage C++
mjn
einev
Télécommunications
mjn
class Cat : public Feline { public : Cat(char *im = "ACAT.GIF", char *nm = "Cat") : Feline(im, nm) {;} virtual Cat() {;} ~ virtual void eat() { cout<<"I eat mices ! "<<endl; } virtual void sleep() { cout<<"ron...ron"<<endl; } virtual void identify(ostream& os) { os<<"Chat : "; Feline::identify(os); } }; class Tiger : public Feline { public : Tiger(char *im = "ATIGER.GIF", char *nm = "Tigre") : Feline(im, nm) {;} virtual ~Tiger() {;} virtual void eat() { cout<<"I eat men ! "<<endl; } virtual void sleep() { cout<<"ron...ron"<<endl; } virtual void identify(ostream& os) { os<<"Tigre : "; Feline::identify(os); } }; class Duck : public Bird { public : Duck(char *im = "ADUCK.GIF", char *nm = "Canard") : Bird(im, nm) {;} virtual ~Duck() {;} virtual void eat() { cout<<"I eat fishes ! "<<endl; } virtual void sleep() { cout<<"ron..coin..ron"<<endl; } virtual void identify(ostream& os) { os<<"Canard : "; Bird::identify(os); } };
void main() { Mouse mickeyMouse("MICKEY.GIF", "Micky Mouse"); Mouse jerry("JERRY.GIF" "Jerry Mouse"); , Cat garfield("GARFIELD.GIF", "Garfield"); Tiger hobbes("HOBBES.GIF", "Hobbes"); Duck donaldDuck("DONALD.GIF", "Donald"); Duck daffyDuck("DAFFY.GIF", "Daffy"); Duck anyDuck; mickeyMouse.identify(cout); cout<<endl; jerry.identify(cout); cout<<endl; hobbes.identify(cout); cout<<endl; donaldDuck.identify(cout); cout<<endl; garfield.identify(cout); cout<<endl; daffyDuck.identify(cout); Le langage C++
223
einev
224
cout<<endl; anyDuck.identify(cout); cout<<endl; }
Télécommunications
Graphiquement, cela donne à peu près ceci :
Le langage C++
mjn
einev Télécommunications
FIGURE 4.Hiérarchie d’animaux
Animal
Le langage C++
Mammifères Rongeurs Souris Félins Chats
Oiseaux
Insectes
Tigres
Canards
est une souris est un rongeur est un mammifère est un animal
est un chat est un félin est un mammifère est un animal
est un tigre est un félin est un mammifère est un animal
est un canard est un oiseau est un animal
est un canard est un oiseau est un animal
mjn
225
einev
14.4Héritage privé
Télécommunications
mjn
L’héritage privé est la deuxième forme d’héritage en C++. Bien que la syntaxe soit très proche, la signification de l’héritage privé est entièrement différente. class A { // définition de la classe de base }; class B :privateA { // Classe dérivée };
L’héritage public définissait une relation de la formeest un. (en anglais ”is a”, ou sim-plement ”isa”). L’héritage privé signifie en revanche ”est implémenté sous forme de”. (En anglias ”is implemented in terms of”). Ainsi, un aiglee st unoiseau, ce qui correspond à l’hé-ritage public. En revanche, pour implémenter une collection d’adresses, on pourrait utiliser un fichier, un tableau ou une liste. Ce qui n’implique aucunement qu’une collection d’adresse soitune liste, un fichier, ou un tableau. Le fait d’utiliser une liste pour implémenter une col-lection d’adresses n’est pas une relation de type ”est un”, et ne correspond donc pas à un hé-ritage public. class List { // classe de base }; class AdressBook :privateList { // classe dérivée };
Dans ce cas, la dérivation n’implique rien de précis pour l’utilisateur de la classe AdressBook. L’ héritage privé n’a d’importance que pour l’implémentateur de la classe dé-rivée (celui qui programmeAdressBook). D’ailleurs, l’utilisateur n’a pas la possibilité d’accéder à la classe de base, car l’une des caractéristiques de l’héritage privé est que tous les membres de la classe de base, quel que soit leur attribut de visibilité, deviennent privés dans la classe dérivée. Dans une dérivation privée, les membres publics de la classe de base deviennent des membres privés de la classe dérivée. Souvent, on peut avantageusement remplacer l’héritage privé par une relation de conte-nance. Rappelons qu’une relation de contenance implique l’appartenance du contenu à l’objet contenant. En C++, on implémente généralement la contenance par un membre privé: Ainsi, pour réutiliser les caractéristiques d’un moteur dans une voiture, qui elle-même dérive d’un type plus générique Véhicule, on pourrait déclarer : class Car : public Vehicle, private Motor { ... };
226
Le langage C++
einev
Télécommunications
Il paraît pourtant plus naturel de déclarer :
class Car : public Vehicle { private : Motor theMotor; ... };
mjn
Ce n’est que dans le cas où la classe dérivée a besoin d’accéder aux membres protégés de la classe de base (par exemple le carburateur du moteur, pour autant que celui-ci soit un membre protégé) que l’héritage privé est nécessaire. Dans la cas contraire, il est généralement préférable d’utiliser une relation de contenancel’héritage privé : ceci peut ré-plutôt que duire les risques d’avoir besoin de recourir, plus tard à de l’héritage multiple (“Héritage mul-tiple”, page245), comme dans l’exemple çi-dessus. Une relation de contenance correspond par sa signification à une relation de type ”possède un” (en anglais ”has a”).
Le langage C++
227
  • Univers Univers
  • Ebooks Ebooks
  • Livres audio Livres audio
  • Presse Presse
  • Podcasts Podcasts
  • BD BD
  • Documents Documents