• Aucun résultat trouvé

Héritage et polymorphisme

N/A
N/A
Protected

Academic year: 2022

Partager "Héritage et polymorphisme"

Copied!
1
0
0

Texte intégral

(1)

Héritage et polymorphisme

La programmation orientée objet est basée sur trois nouveaux concepts : - Encapsulation (déjà vu et travaillé dans les travaux pratiques) - Héritage ( tel père => tel fils et plus!!! )

- Polymorphisme (capacité de se présenter sous plusieurs formes différentes : la lampe magique d’Aladin???)

Héritage :

La POO favorise la réutilisation des classes, des méthodes, du codage. Grâce à l'héritage, on peut modifier une classe sans avoir à la réécrire complètement.

L’héritage peut être simple ou multiple.

Héritage simple:une classe peut hériter d’une seule super-classe

Point

PointVisible

PointVisibleColore

Un point visible est un point.

Un point visible avec couleur est un point visible.

La classe PointVisible hérite (ou dérive) de la classe Point.

La classe dont on dérive est dite classe de base ou super-classe.

Les classes obtenues par dérivation sont dites classes dérivées ou sous- classes.

La classe dérivée modélise un cas particulier de la classe de base, et est donc enrichie d’informations supplémentaires.

(2)

La classe dérivée possède les propriétés suivantes : - contient les données de la classe de base, - peut posséder de nouvelles,

- possède les méthodes de la classe de base, - peur redéfinir (masquer) certaines méthodes, - peut posséder de nouvelles méthodes.

Contrôle d’accès :

Les droits d’accès protègent les données et les méthodes et réalisent aussi l’encapsulation :

- un membre public est accessible à toute fonction

- un membre private n’est accessible qu’aux fonctions membres de la classe ou aux fonctions amies

- un membre protected n’est accessible qu’aux fonctions membres de la classe de base ou des classes dérivées ou aux fonctions amies.

Syntaxe de l’héritage :

class classe_dérivée :protection classe_de_base { etc };

Les types de protections sont : 1) public : (le plus utilisé)

Il donne accès aux membres publiques et protégés de la classe de base.

Ces membres ont le même statut dans la classe dérivée.

2) protected : (rarement utilisé)

Il donne accès aux membres publiques et protégés de la classe de base.

Ces membres deviennent tous protégés dans la classe dérivée.

3) private : (dans ce cas, la notion d’héritage disparaît)

Il donne accès aux membres publiques et protégés de la classe de base.

Ces membres deviennent privés dans la classe dérivée.

Dans tous les cas, les membres privés de la classe de base ne sont jamais accessibles par les membres des classes dérivées

.

#include <iostream.h>

#include <math.h>

class Point

{ private : // cet accès reste privé dans la dérivée public float x ;

protected : // cet accès reste protégé dans la dérivée public float y ;

(3)

public :

Point(float x, float y) { this->x = x ; this->y = y ; }

Point() {}

float getX() const { return x ; } // pour accéder aux membres privés void afficher(char * message) {

cout << message << endl ;

cout << " - abscisse " << x << endl ;

cout << " - ordonnee " << y << endl << endl;

} } ;

// classe PointVisible, dérivée public de Point class PointVisible : public Point

{ private :

bool visible ; public :

// appel du constructeur de la classe de base

PointVisible(float a, float o, bool vis = false) : Point(a, o) { visible = vis;

}

// accès à l'abscisse : par get, à ordonnée : direct (membre protégé) double distance() {

return sqrt( getX()*getX() + y*y );

} };

class PointVisibleColore : public PointVisible { private:

int colore ; public:

PointVisibleColore(float a, float o, bool vis, int co) : PointVisible(a,o,vis) {

colore = co;

}

};

Redéfinition des fonctions membres :

La fonction membre afficher de la classe de base Point n’affiche que les membres de cette classe. On ne peut donc pas afficher le membre visible de la classe dérivée PointVisible.

(4)

Pour cela, nous allons définir dans la classe dérivée une fonction portant le même nom, et qui aura pour rôle d’afficher les données de la classe dérivée.

On parle alors de redéfinition d’une fonction de la classe de base.

class PointVisible : public Point { private :

bool visible ; public :

// appel du constructeur de la classe de base

PointVisible(float a, float o, bool vis = false) : Point(a, o) { visible = vis;

}

// accès à l'abscisse : par get, à ordonnée : direct (membre protégé) double distance() {

return sqrt( getX()*getX() + y*y );

}

// Redéfinition de la fonction afficher void afficher(char * message) {

cout << " - abscisse " << x << endl ;

cout << " - ordonnee " << y << endl << endl;

cout << " - visible : " << ( visible ? "oui":"non") << endl;

cout << " - distance: " << distance() << endl << endl;

} };

La fonction afficher dans ce cas-là ne vas pas fonctionner, il y aura erreur de compilation ; la classe PointVisible n’a pas le droit d’accéder aux membres privés (ici x) de la classe de base Point.

Comment afficher alors les données de la classe de base et celles de la classe dérivée et cela par l’utilisation d’une fonction dans la classe dérivée?

class PointVisible : public Point { private :

bool visible ; public :

// appel du constructeur de la classe de base

PointVisible(float a, float o, bool vis = false) : Point(a, o) { visible = vis;

}

// accès à l'abscisse : par get, à ordonnée : direct (membre protégé) double distance() {

return sqrt( getX()*getX() + y*y );

}

(5)

void afficher(char * message) {

Point ::afficher(message); //appel de afficher de la classe Point cout << " - visible : " << ( visible ? "oui":"non") << endl;

cout << " - distance: " << distance() << endl << endl;

} };

Puisque afficher de la classe de base est accessible, on fera donc appel à elle à partir de la fonction afficher de la classe dérivée.

Constructeurs et destructeurs:

Pour construire un PointVisible, il faut construire d’abord un Point.

Le constructeur de la classe de base (Point) est donc appelé avant le constructeur de la classe dérivée (PointVisible).

De façon symétrique, le destructeur de la classe de base (Point) est appelé après le destructeur de la classe dérivée (PointVisible).

Si la classe de base a un constructeur autre que celui par défaut, la classe dérivée doit avoir un constructeur, sinon il est impossible de créer un objet.

Si dans l’appel de constructeur de la classe dérivée, le nom de constructeur de la classe de base n’est pas mentionné explicitement, le constructeur par défaut de la classe de base sera pris en compte. Si la classe de base ne possède pas ce constructeur, il y aura alors une erreur de compilation.

Polymorphisme :

Le mot polymorphisme est issu d'un mot grec signifiant "qui possède plusieurs formes" (la lampe magique d'Aladin : Maître, sous quelle forme voulez-vous que je me transforme?).

En C++, le but de polymorphisme est de permettre d'identifier clairement lequel des champs, lequel des objets, laquelle des méthodes qu'on travaille lors d'une possibilité de confusion dans leur utilisation.

Il y a trois cas de polymorphisme :

1) Autoréférence this (this pointe vers l’objet courant)

2) Appel d'une méthode de la classe de base quand la classe dérivée dispose d'une méthode portant le même nom (redéfinition d’une méthode).

3) Cas de fonctions virtuelles (voir prochaine section)

(6)

Exemple :

/*

Dans l'exemple :

TriangleRectangulaire est dérivée de Rectangle, respecte la syntaxe mais ne profite nullement de sa classe de base Triangle

TriangleEquilateral (3 côtés égaux) est une autre classe dérivée de Triangle. Elle profite de certaines méthodes de Triangle (constructeurs, Périmètre, affichage, ...)

TriangleIsocele (2 côtés égaux) dérive de TriangleEquilateral qui ne dispose pas de l'affichage => on utilise affichage de sa classe de base

*/

#include <iostream.h>

#include <math.h>

class Triangle {

protected :

double cote1, cote2, cote3 ;

public :

Triangle (double cote1, double cote2, double cote3) {

this->cote1 = cote1 ; // cas 1 de polymorphisme this->cote2 = cote2 ;

this->cote3 = cote3 ; }

Triangle () {}

void afficher(char * message) { cout << message ;

cout << "<" << cote1 << ", "

<< cote2 << ", " << cote3 << ", perimetre : "

<< perimetre() << ">\n\n";

}

double perimetre() { return cote1 + cote2 + cote3 ; }

} ;

class TriangleRectangulaire : public Triangle {

private :

double hauteur, base ;

(7)

public :

TriangleRectangulaire (double h, double b) { hauteur = h;

base = b;

}

TriangleRectangulaire () {}

double surface() {

return hauteur * base / 2.0 ; }

double perimetre() {

return hauteur + base + sqrt(hauteur*hauteur + base * base);

}

void afficher(char * message) { cout << message ;

cout << "<hauteur :" << hauteur << ", base : "

<< base << ", perimetre : "

<< perimetre() << ", surface : " << surface() << ">\n\n";

}

} ;

class TriangleEquilateral : public Triangle {

private :

double cote ;

public :

// on peut construire un triangle régulier (3 côtés valent c) TriangleEquilateral (double c): Triangle(c, c, c) {

cote = c;

}

TriangleEquilateral () {}

double surface() {

return cote * (cote / 2) / 2.0 ; }

void afficher(char * message) { cout << message ;

cout << "<cote " << cote << ", surface : " << surface() << ">\n";

(8)

// cas 2 du polymorphisme

Triangle::afficher("(par afficher de la classe Triangle)");

}

} ;

class TriangleIsocele : public Triangle { private :

double cote, base ;

public :

TriangleIsocele (double c, double b): Triangle(c, c, b) {

cote = c;

base = b;

}

// elle ne dispose pas de l'affichage } ;

void main()

{ Triangle t1(5, 10, 20);

t1.afficher("Informations du triangle regulier t1 :");

TriangleRectangulaire t2(10, 6);

t2.afficher("Informations du triangle rectangulaire t2 :\n");

TriangleEquilateral t3(10);

t3.afficher("Infos du triangle equilateral t3:\n");

TriangleIsocele t4(12, 5);

t4.afficher("(avec afficher de la classe de base Triangle)");

}

/* Exécution :

Informations du triangle regulier t1 :<5, 10, 20, perimetre : 35>

Informations du triangle rectangulaire t2 :

<hauteur :10, base : 6, perimetre : 27.6619, surface : 30>

Infos du triangle equilateral t3:

<cote 10, surface : 25>

(par afficher de la classe Triangle)<10, 10, 10, perimetre : 30>

(avec afficher de la classe de base Triangle)<12, 12, 5, perimetre : 29>

(9)

Press any key to continue*/

Fonctions virtuelles :

Considérons une classe dérivée Derivee d’une classe de base Base : class Base {

private :

int age ; public :

Base(int k) { age = k ; }

Base(){}

int getAge() { return age; } void afficher() {

cout << "afficher dans Base : " << age << endl << endl;

} };

class Derivee : public Base { private :

double taille ; public :

Derivee(int k, double t) : Base(k) { taille = t ;

}

void afficher() {

cout << "afficher dans Derivee : " << getAge()

<< " an(s) " << taille << " metre " << endl << endl;

} };

Avec les déclarations : Base pers1(23);

Derivee pers2(28, 1.72);

Base * p1 = &pers1 ;

p1->afficher(); appelle la méthode afficher() de la classe Base.

Si on fait (autorisé) : p1=&pers2;

(10)

p1 pointe sur un objet de la classe Derivee. L'instruction p1->afficher(); va faire toujours appel à la méthode afficher de la classe Base, alors que Derivee possède lui aussi une méthode afficher.

Raison: Le choix de la méthode à appeler a été réalisé lors de la compilation; il a donc été fait en fonction du type de l’objet p1.

On parle alors de ligature statique.

Le typage statique est le type par défaut en C++

Si on veut que p1 -> afficher() appelle non plus systématiquement la méthode afficher de la classe Base, mais celle correspondant au type de l'objet réellement désigné par p1 (Derivee) on doit utiliser une ligature dynamique : Le choix se fera à l'exécution et donc la fonction est sélectionnée en fonction de la classe pointée par le pointeur p1.

Le mot clé virtual force la ligature dynamique : class Base {

private :

int age ; public :

Base(int k) { age = k ; }

Base(){}

int getAge() { return age; } virtual void afficher() {

cout << "afficher dans Base : " << age << endl << endl;

} };

class Derivee : public Base { private :

double taille ; public :

Derivee(int k, double t) : Base(k) { taille = t ;

}

//il n’est pas nécessaire de déclarer dans une classe dérivée // le mot virtual devant une méthode déclarée virtual dans la // classe de base

void afficher() {

cout << "afficher dans Derivee : " << getAge() << " an(s) " << taille << " metre "

<< endl << endl;

} };

(11)

void main() {

Derivee pers2(28, 1.72);

Base * p1 = &pers2;

p1->afficher();

}

/* Exécution :

afficher dans Derivee : 28 an(s) 1.72 metre

*/

Autre exemple des fonctions virtuelles :

#include <iostream.h>

#include <math.h>

class Point { private :

int x, y ; public:

Point() : x(0), y(0) { }

Point(int a, int b) : x(a), y(b) { }

double longueur () {

return sqrt( x * x + y * y);

}

virtual void infoDeBase(char * message) { cout << "Point " << message ; }

void afficher(char * message) { infoDeBase(message);

cout << "<x = " << x << ", y = " << y

<< ", longueur = " << longueur() << ">\n\n";

} };

class PointColore : public Point { private :

bool avecCouleur ; public :

PointColore(){

(12)

avecCouleur = true;

}

PointColore (int abs, int ord, bool c) : Point(abs, ord) { avecCouleur = c ;

}

void infoDeBase(char * message) { cout << "Point " << message

<< ( avecCouleur ? " avec couleur ": " sans couleur ");

} };

class PointVisible : public Point { private :

bool estVisible ; public :

PointVisible(){

estVisible = false;

}

PointVisible (int abs, int ord, bool v) : Point(abs, ord) { estVisible = v ;

}

void infoDeBase(char * message) { cout << "Point " << message

<< ( estVisible ? " est visible ": " est invisible ");

} };

void main() {

Point A(12, 6);

A.afficher("A");

PointColore B(5, 8, true);

B.afficher("B");

PointVisible C(30, 18, false);

C.afficher("C");

}

/* Exécution :

Point A<x = 12, y = 6, longueur = 13.4164>

Point B avec couleur <x = 5, y = 8, longueur = 9.43398>

(13)

Point C est invisible <x = 30, y = 18, longueur = 34.9857>

Press any key to continue

*/

Classes abstraites :

Pourquoi ?

Lors de la conception d'une hiérarchie, on a besoin de créer des classes plus générales d'abord et différer l'implémentation à des étapes plus éloignées quand le comportement est très bien défini.

Définition : Une classe est dite abstraite si elle contient au moins une fonction virtuelle pure (méthode abstraite) :

class X {

// afficher est une fonction virtuelle pure virtual void afficher() =0;

};

Règles à respecter :

1) Pas d'instanciation : trop abstraite pour la construction d'un objet 2) Une classe abstraite n’existe que pour être héritée.

3) Une fonction virtuelle pure (méthode abstraite) déclarée dans la classe abstraite doit être implémentée dans une de ses

sous-classes (pas nécessairement dans toutes ses sous-classes) :

class Y : public X {

void afficher() {

cout << "implementer afficher dans Y" << endl;

}

(14)

};

Exemple :

classe abstraite Figure géométrique

peut-on calculer :

1) le périmètre de n'importe quelle figure ? non! => ce calcul est abstrait!

2) la surface de n'importe quelle figure ? non! => ce calcul est abstrait!

classe

Parallelogramme

périmètre : Oui

2x(cote1+cote2)

surface : Non encore abstraite

classe Cercle périmètre : Oui

2xPixrayon

surface : Oui

Pi x rayon 2 classe concrète

classe Triangle périmètre : Non surface :

hauteur x base / 2

encore abstraite

classe Rectangle

surface : Oui longueur x largeur classe concrète

classe

TriangleRectangle

périmètre : Oui

Hauteur + base+ sqrt(hauteur*

hauteur + base * base) classe concrète

#include <iostream.h>

#include <math.h>

const double PI=3.14;

class FigureGeometrique { public:

virtual double perimetre()=0;

virtual double surface()=0;

virtual void identifier(char * message) {

cout << "FigureGeometrique: " << message << endl;

}

(15)

identifier(message);

cout << "-perimetre: " << perimetre() << endl;

cout << "-surface: " << surface() << endl;

} };

class Cercle :public FigureGeometrique { private:

double rayon;

public:

Cercle(double r) { rayon=r;

}

void identifier(char *message) {

cout << "Cercle: " << message << endl;

}

double perimetre() { return 2* PI*rayon;

}

double surface() {

return PI*rayon*rayon;

} };

class Parallelogramme: public FigureGeometrique { protected:

double cote1, cote2;

public:

Parallelogramme(double cote1, double cote2) { this->cote1=cote1;

this->cote2=cote2;

}

void identifier(char *message) {

cout << "Parallelogramme: " << message << endl;

}

double perimetre() {

return 2*(cote1+cote2);

}

double surface()=0;

};

class Rectangle:public Parallelogramme { public:

Rectangle(double longueur, double largeur):

Parallelogramme(longueur,largeur) { };

void identifier(char *message) {

cout << "Rectangle: " << message << endl;

}

double surface() {

(16)

return cote1*cote2;

} };

class Triangle: public FigureGeometrique { protected:

double hauteur, base;

public:

Triangle(double hauteur, double base) { this->hauteur=hauteur;

this->base=base;

}

void identifier(char *message) {

cout << "Triangle: " << message << endl;

}

double perimetre()=0;

double surface() {

return hauteur*base/2.0;

} };

class TriangleRectangle: public Triangle { public:

TriangleRectangle(double hauteur, double base) : Triangle(hauteur,base) {

}

void identifier(char *message) {

cout << "TriangleRectangle: " << message << endl;

}

double perimetre() {

return hauteur + base + sqrt(hauteur*hauteur + base * base);

} };

void main() {

Cercle C(8.7);

C.afficher("C");

cout << endl;

Rectangle Rect(5.2,3.2);

Rect.afficher("Rect");

cout << endl;

TriangleRectangle TrRect(9.8,5.8);

TrRect.afficher("TrRect");

}

(17)

/*Exécution Cercle: C

-perimetre: 54.636 -surface: 237.667 Rectangle: Rect -perimetre: 16.8 -surface: 16.64

TriangleRectangle: TrRect -perimetre: 26.9877

-surface: 28.42

Press any key to continue

*/

Héritage multiple :

Héritage simple : classe dérivée n’a qu’une seule classe de base Héritage multiple : classe dérivée a plus d’une classe de base

super classe

A1 super classe

A2 super classe

A3 etc …

classe dérivée B BB

Exemple 1 : Gestion des fichiers en C++

ios

istream ostream

ifstream iostream ofstream

fstream

ifstream : gestion des fichiers de données, dérivée de istream ofstream : gestion des fichiers de sorties, dérivée de ostream

(18)

iostream : dérivée de istream et ostream (héritage multiple) fstream : dérivée de iostream

*/

Fichier fstream.cpp

- créer un fichier binaire avec 15 entiers aléatoires - relire le fichier binaire

*/

#include <iostream.h>

#include <fstream.h> // pour la gestion des fichiers

#include <iomanip.h>

#include <time.h>

#include <stdlib.h> // pour srand et rand : val. aléatoires

// créer un fichier binaire d'un nom donné avec un "nombre" d'entiers void creer(char * nom, int nombre)

{

// appel un constructeur de la classe ofstream pour ouvrir un fichier // en mode sortie

ofstream sortie(nom, ios::out);

srand(time(NULL)); //pour rendre des nombres plus aléatoirement possible for (int i = 0 ; i < nombre ; i++) {

int valeur = rand() % 30000 ; // un entier arbitraire entre 0 et 29999 // écrire sur l'objet "sortie" (version : voir la syntaxe)

sortie.write( (char *) &valeur, sizeof(valeur));

}

sortie.close(); //fermer le fichier :OBLIGATOIRE pour un fichier à créer cout << "Fin de la creation du fichier " << nom << endl << endl ;

}

// relire (par programmation) le fichier binaire créé:

void relire(char * nom) {

// construire un objet (entree) de la classe ifstream (en mode lecture) ifstream entree(nom, ios::in);

int k = 0, valeur, nbBytes = sizeof(int) ;

cout << "\nContenu du fichier " << nom << ": " << endl << endl ; while ( entree.read((char *) &valeur, nbBytes) )

cout << setw(3) << ++k << ") "

<< setw(6) << valeur << endl ; entree.close(); // fortement recommandé

}

(19)

void main() {

creer("Entiers.Bin", 15);

relire("Entiers.Bin");

}

/* Exécution :

Fin de la creation du fichier Entiers.Bin

Contenu du fichier Entiers.Bin:

1) 23250 2) 2036 3) 27565 4) 22087 5) 4037 6) 25492 7) 25280 8) 17189 9) 24363 10) 3752 11) 1007 12) 11668 13) 5677 14) 26104 15) 27462

Press any key to continue

*/

Exemple 2 :

// Fichier her_mul1.cpp : exemple simple de l'héritage multiple

#include <iostream.h>

#include <math.h>

class Point { private :

int x, y ; public:

Point() : x(0), y(0) { }

Point(int a, int b) : x(a), y(b) {

(20)

}

double longueur () {

return sqrt( x * x + y * y);

}

void afficher() {

cout << "Point : <x = " << x << ", y = " << y

<< ", longueur = " << longueur() << ">\n\n";

} };

class CouleurVisible { private :

bool avecCouleur, estVisible ; public :

CouleurVisible(bool c = false, bool v = false) { avecCouleur = c ;

estVisible = v ; }

void afficher() {

cout << ( estVisible ? " est visible ": " est invisible ") << endl;

cout << ( avecCouleur ? " colore ": " incolore ") << endl;

} };

class PointColoreVisible : public Point, public CouleurVisible { public :

PointColoreVisible(int abs, int ord, bool c, bool v) : Point(abs, ord), CouleurVisible(c, v) {

}

void afficher() {

Point::afficher();

CouleurVisible::afficher();

} };

void main() {

Point A(12, 6);

A.afficher();

PointColoreVisible B(20, 50, false, true);

(21)

B.afficher();

}

/*Exécution

Point : <x = 12, y = 6, longueur = 13.4164>

Point : <x = 20, y = 50, longueur = 53.8516>

est visible incolore *

Exemple 3:

/*Fichier her_mul2.cpp

ListeLineaire Vecteur

ListeVecteur

*/

#include <iostream.h>

#include <math.h>

struct Element { void * contenu ; Element * suivant ; };

class ListeLineaire { private :

Element * debut, * courant ; public :

ListeLineaire() {

debut = courant = NULL;

}

~ListeLineaire() ; void ajouter(void *);

void premier() {

courant = debut ;

}

void * prochain() {

void * tempo = NULL;

if (courant) {

(22)

tempo = courant->contenu;

courant = courant->suivant;

}

return tempo;

}

bool fin() {

return (courant == NULL);

} };

ListeLineaire::~ListeLineaire() { courant = debut ;

while (courant) {

Element * suiv = courant->suivant ; delete courant;

courant = suiv;

} }

void ListeLineaire::ajouter(void * item) { Element * tempo = new Element ;

tempo->suivant = debut;

tempo->contenu = item ; debut = tempo;

}

class Vecteur

{ int x, y, z ; public :

Vecteur() {}

Vecteur(int, int, int = 0) ;

double longueur() { return sqrt(x*x + y*y + z*z); } void afficher();

};

Vecteur::Vecteur(int a, int b, int c) { x = a ;

y = b ; z = c ; }

void Vecteur::afficher() {

cout <<"Les coordonnees du vecteur : " << x << " " << y << " " << z << endl;

(23)

}

class ListeVecteur : public ListeLineaire, public Vecteur { public :

ListeVecteur() {}

void afficher();

};

void ListeVecteur::afficher() { premier();

if ( fin() )

cout << "La liste est vide\n\n";

else{

cout << "Contenu de la liste :\n";

while ( ! fin() ) {

Vecteur * ptr = (Vecteur *) prochain();

ptr->afficher();

}

cout << "\nFin de la liste\n" << endl ; }

}

void main() {

Vecteur v1(5,10, 15), v2(6, 4, 1), v3(5,4, 9), v4(12, 6, 18);

ListeVecteur tete ; tete.ajouter(&v1);

tete.ajouter(&v2);

tete.ajouter(&v3);

tete.ajouter(&v4);

tete.afficher();

}

/* Exécution :

Contenu de la liste :

Les coordonnees du vecteur : 12 6 18 Les coordonnees du vecteur : 5 4 9 Les coordonnees du vecteur : 6 4 1 Les coordonnees du vecteur : 5 10 15 Fin de la liste

Press any key to continue

*/

(24)

Classes virtuelles :

Considérons la situation suivante :

A A

B C

D class A {

protected:

int x,y;

… };

class B : public A {…};

class C : public A {…};

class D : public B, public C {…};

void main() { D d;

d.x=0; //ERREUR, ambiguïté

d.B::x=1; //OK

d.C::x=2; //OK

}

En quelque sorte, D hérite 2 fois de A ! C'est-à-dire que les membres de A (méthodes ou attributs) apparaissent deux fois dans D. Les attributs x et y seront dupliqués pour chaque instance de D.

En général on ne désire pas avoir cette duplication des données Il est possible de n'avoir qu'une occurrence des membres de la classe de base, en déclarant la classe A comme virtuelle.

Pour que la classe D n'hérite qu'une seule fois de la classe A, il faut que les classes B et C héritent virtuellement de A.

A

B C

D class A {

protected:

int x,y;

… };

(25)

class B : virtual public A {…};

class C : virtual public A {…};

class D : public B, public C {…};

void main() { D d;

d.x=0; //OK pas d'ambiguïté }

Appel des constructeurs dans l’héritage multiple :

1) Classes non virtuelles :

Les appels se font dans l’ordre suivant :

- constructeur de la classe de base dans l’ordre d’appel, - constructeur classe dérivée.

2) Classes virtuelles :

Les appels se font dans l’ordre suivant :

- constructeur de la classe virtuelle avant toutes les classes, - ordre précédent (classes non virtuelles).

Références

Documents relatifs

De plus, une interface contient souvent ses méthodes abstraites qui présentent certaines comportements assez générales ( hasMoreElements() : avoir encore

• const C::`vftable' qui fournit l'implémentation de la fonction redéfinie GetInt() de la classe A et utilise la fonction de base SetInt() de la classe A,.. • const C1::`vftable'

- certains produits fabriqués à partir des matières comprises dans cette classe non classés par ailleurs selon leur fonction ou leur destination, par exemple : les pique-fleurs

o classe de base ou classe mère, la classe plus abstraite ; o classe dérivée ou classe fille, la classe plus spécifique.. Toutes les instances de B sont aussi des instances

public class Voiture extends Vehicule { private Roue[] roue = new

public class NomComplet extends Nom { private String prenom;// nouvelle var.

Peintures, vernis, laques; produits antirouille et produits contre la détérioration du bois; colorants, teintures; encres d'imprimerie, encres de marquage et encres de gravure;

6- Ecrire la méthode void avancerAvecConflit(int n, Pion pio), qui fait la même chose que la méthodes avancer de la classe Pion et qui prend compte de cette modification (la