Nous allons réutiliser notre exemple du chapitre 6, où nous avions donné deux implantations différentes dun type liste
reproduisant (extérieurement) une liste chaînée. La représentation interne de ces classes nétait pas forcément une vraie liste chaînée.
Il se peut que dans un même programme on souhaite disposer des deux types de listes chaînées. Une première solution consiste à donner des noms différents aux deux classes (et non identiques comme on la fait au chapitre 6), et à utiliser lune ou lautre selon les besoins.
Cela pose toutefois des problèmes spécifiques. En effet, si lon veut par exemple utiliser un tableau de listes (ou plutôt de pointeurs de listes), il faudra choisir un des deux types, et faire constamment des changements de types, avec les risques que cela suppose.
Une méthode bien meilleure consiste à faire dériver lune des classes de lautre, comme ceci :
class liste { noeud* courant; protected : int nombre; public : liste() { nombre = 0; courant = 0; } liste(int n, const element*); // consructeur avec table virtual ~liste(); virtual void avance(int combien = 1); void recule(int combien = 1) { avance(-combien); } virtual element& valeur(void) { if (courant) return courant->contenu(); } unsigned nombre_elt(void) { return nombre; } void affiche(unsigned combien = 65535); virtual int insere(const element&); virtual void supprime(int n = 1); };class listetab : public liste { element *tab, *courant; public : listetab() { courant = tab = 0; } listetab(int n, const element*); // consructeur avec table ~listetab(); void avance(int combien = 1); element& valeur(void) { if (courant) return *courant; } int insere(const element&); void supprime(int n = 1); };
On observe dabord que lon gagne du temps et du code, puisque certaines méthodes nont pas besoin dêtre redéfinies ; il suffit généralement de bien choisir les méthodes virtuelles.
Exercice 8.1 | On a supposé que la méthode daffichage utilisait la méthode |
Voir solution |
Exercice 8.2 | Quelles sont les méthodes virtuelles et celles qui ne le sont pas dans les classes |
Voir solution |
Cela fait, rien nempêche plus de définir un tableau de pointeurs :
element table[5] = { 1, 3, 5, 7, 11 };liste *listes[3] = { new liste(5, table), new listetab(2, table +3), new listetab(3, table) };for (int i = 0; i < 3; i++) if (listes[i]) listes[i]->affiche();// ...for (i = 0; i < 3; i++) delete listes[i];
Bien remarquer que ce sont les bonnes fonctions daffichage et de destruction qui sont appelées dans cet exemple.
Exercice 8.3 | Quel est laffichage produit par cet exemple ? Quelle est la place mémoire totale occupée dans le tas, en fonction de la taille S = |
Voir solution |
Linconvénient de cette méthode simple, cest que le champ noeud*
est inutilisé dans listetab
, ce qui gaspille de la mémoire (en faible quantité cependant). Évidemment, on aurait pu le réutiliser pour tenir le rôle de tab
par exemple, mais cela aurait exigé de désagréables changements de types à tout moment. En outre, il aurait fallu le déclarer protégé et non privé. Et il est clair que dans tous les exemples la situation ne sera pas si simple.
Un autre inconvénient important résulte de ce quil devient impossible de définir le type listetab
avant lautre, et difficile de recommencer avec de nouveaux types de listes. Pour régler ce problème plus élégamment, le langage fournit les classes abstraites.
Précédent | Sommaire | Suivant |