• Aucun résultat trouvé

12. L'HÉRITAGE EN LANGAGE C++

12.9 MÉTHODES VIRTUELLES PURES - CLASSES ABSTRAITES .1 Définitions

12.9.2 Conteneur et objets polymorphiques

On appelle conteneur une structure de données pouvant en contenir d'autres, quel que soit leur type.

Exemple : conteneur d'objets polymorphiques

 Un sac est un conteneur pouvant contenir plusieurs objets, non forcément uniques. Un objet peut donc être placé plusieurs fois dans le sac.

 On définit deux fonctions permettant respectivement de mettre et de retirer un objet d'un sac ainsi qu'une fonction permettant de détecter la présence d'un objet dans le sac.

 Une classe abstraite est la classe mère de tous les objets utilisables.

 Le conteneur n'utilise que des pointeurs sur la classe abstraite ce qui permet son utilisation dans toutes les classes dérivées. Deux objets identiques sont différenciés par un numéro unique dont le choix est à leur charge. La classe abstraite dont ils dérivent est donc dotée d'une méthode le retournant.

 Les objets sont affichés dans un format spécifique par la fonction print, méthode virtuelle pure de la classe abstraite.

#include <iostream.h>

/************* LA CLASSE ABSTRAITE DE BASE *****************/

class Object

{unsigned long int h; // Handle de l'objet.

unsigned long int new_handle(void);

public:

Object(void); // Le constructeur

virtual ~Object(void); // Le destructeur virtuel virtual void print(void) =0; // méthode virtuelle pure

unsigned long int handle(void) const; // Fonction retournant le numéro d'identification de l'objet };

// Cette fonction n'est appelable que par la classe Object : unsigned long int Object::new_handle(void)

{static unsigned long int hc = 0;

163

hc++;

return hc; // hc handle courant, incrémenté à chaque appel de

// new_handle }

// Le constructeur de la classe Object doit être appelé par les classes dérivées Object::Object(void)

{h = new_handle(); // allocation d'un nouveau handle.

}

Object::~Object(void) {}

unsigned long int Object::handle(void) const

{return h;} // Retourne le numéro de l'objet.

/******************** LA CLASSE SAC ******************/

class Bag : public Object // La classe sac hérite de la classe Object, // car un sac peut en contenir un autre. Le sac // est implémenté sous la forme d'une liste chaînée {typedef struct baglist

{baglist *next;

Object *ptr;

}BagList;

BagList *head; // La tête de liste.

public:

Bag(void); // constructeur

~Bag(void); // destructeur

void print(void); // fonction d'affichage du contenu du sac.

bool has(unsigned long int) const; // booléen vrai si le sac contient l'objet.

bool is_empty(void) const; // booléen vrai si le sac est vide.

void add(Object &); // ajout d'un objet dans le sac void remove(Object &); // retrait d'un objet du sac Bag (const Bag &Source){}

Bag & operator = (const Bag &source)

{BagList *tmp = head; // réinitialisation du pointeur this while (tmp != (BagList *) NULL)

{tmp = tmp->next; delete head; head = tmp; }

BagList *tmp2 = source.head; // copie de la liste des objets de source while (tmp2 != (BagList *) NULL)

{this->add(*(tmp2->ptr)); tmp2 = tmp2->next; } return *this;

} };

164

Bag::Bag(void) : Object() {}

Bag::~Bag(void)

{BagList *tmp = head; // destruction de la liste des objets

cout <<" Destructeur Bag" << endl;

while (tmp != (BagList *) NULL)

{tmp = tmp->next; delete head; head = tmp; } }

void Bag::print(void) {BagList *tmp = head;

cout << "Sac n° " << handle() << ".\n Contenu : \n";

while (tmp != (BagList *)NULL) {cout << "\t";

tmp->ptr->print(); // affichage de la liste des objets.

tmp = tmp->next;

} }

bool Bag::has(unsigned long int h) const {BagList *tmp = head;

while (tmp != NULL && tmp->ptr->handle() != h) tmp = tmp->next; // Cherche l'objet.

return (tmp != NULL);

}

bool Bag::is_empty(void) const {return (head==NULL);}

void Bag::add(Object &o) // Ajout d'un objet à la liste {BagList *tmp = new BagList;

tmp->ptr = &o;

tmp->next = head;

head = tmp;

}

void Bag::remove(Object &o)

{BagList *tmp1 = head, *tmp2 = NULL;

while (tmp1 != NULL && tmp1->ptr->handle() != o.handle())

{tmp2 = tmp1; // recherche de l'objet

tmp1 = tmp1->next;

}

165

if (tmp1!=NULL) // suppression de la liste.

{if (tmp2!=NULL) tmp2->next = tmp1->next;

else head = tmp1->next;

delete tmp1;

} }

class MonObjet : public Object {public :

int entier;

MonObjet(){entier=0;}

MonObjet(int donnee){entier=donnee;}

void print(void);

};

void MonObjet::print(void) {cout <<entier << endl;}

int main(void)

{Bag *MonSac = new Bag;

Bag *MonSac2 = new Bag;

MonObjet a(1), b(5), c(8), d(10), e(3);

cout << "On ajoute a=1 b=5 c=8 b=5 d=10 à sac n°1" << endl;

MonSac->add(a);

MonSac->add(b);

MonSac->add(c);

MonSac->add(b);

MonSac->add(d);

MonSac->print();

cout << "On enleve b à sac n°1" << endl;

MonSac->remove(b);

MonSac->print();

// appel récursif

cout << "On copie Sac 1 dans Sac 2 (copie inversée)" << endl;

*MonSac2 = *MonSac;

MonSac2->print(); // appel récursif cout << "On ajoute Sac 2 à Sac 1" << endl;

MonSac->add(*MonSac2);

MonSac->print(); // appel récursif delete MonSac;

delete MonSac2;

166

return 0;

}

// résultat

On ajoute a=1 b=5 c=8 b=5 d=10 à sac n°1 Sac n° 1.

Contenu : 10 5 8 5 1

On enleve b à sac n°1 Sac n° 1.

Contenu : 10 8 5 1

On copie Sac 1 dans Sac 2 (copie inversée) Sac n° 2.

Contenu : 1 5 8 10

On ajoute Sac 2 à Sac 1 Sac n° 1.

Contenu : Sac n° 2.

Contenu : 1 5 8 10 10 8 5 1

Destructeur Bag Destructeur Bag

167

Remarques

 La classe de base sert de cadre (frame) aux classes dérivées.

 La classe Bag permet de stocker des objets dérivant de la classe Object avec les méthodes add et remove.

 La faculté d'interdire à une méthode virtuelle pure définie dans une classe dérivée d'accéder en écriture aux données de la classe de base et à celles de sa classe peut faire partie de ses prérogatives par qualification const du pointeur this (pointeur constant sur un objet constant). Ainsi, dans l'exemple ci-dessous, la méthode virtuelle pure print qualifiée const permet l'accès en lecture seule à l'objet h.

 Les autres méthodes ont un accès total, y compris celles des classes dérivées qui ne surchargent pas une méthode de la classe de base.

 Cette méthode d'encapsulation est coopérative et permet de détecter des erreurs à la compilation. Elle peut s'appliquer aux méthodes virtuelles non pures ou aux fonctions non virtuelles.

Exemple class Object

{unsigned long int new_handle(void);

protected: // Héritage de la classe Object qualifié protected

unsigned long int h;

public:

Object(void); // Le constructeur de la classe Object

virtual void print(void) const=0; // Méthode virtuelle pure qualifiée constante

unsigned long int handle(void); // Méthode retournant le numéro d'identification de l'objet };

168