Jeux Libres
       
           

» Les Tutoriels » Apprenez à programmer en C++ ! » Le polymorphisme

Note : Vous vous apprêtez à lire un tutoriel de M@teo21 et Nanoc initialement publié à cette adresse sous la licence Creative Commons BY-NC-SA 2.0.

Le polymorphisme


Vous avez bien compris le chapitre sur l'héritage ? C'était un chapitre relativement difficile. Je ne veux pas vous faire peur mais celui que vous êtes en train de lire est du même acabit. C'est sans doute le chapitre le plus complexe de tout le cours mais vous allez voir qu'il va nous ouvrir de nouveaux horizons très intéressants.

Mais au fait, de quoi allons-nous parler ? Le titre est simplement « le polymorphisme », ce qui ne nous avance pas vraiment.
Si vous avez fait un peu de grec, vous êtes peut-être à même de décortiquer ce mot. « Poly » signifie « plusieurs », comme dans polygone ou polytechnique, et « morphe » signifie « forme » comme dans euh amorphe ou zoomorphe.
Nous allons donc parler de choses ayant plusieurs formes. Ou, pour utiliser des termes informatiques, nous allons créer du code fonctionnant de différentes manières selon le type qui l'utilise.

Je vous conseille vivement de relire le les pointeurs chapitre sur  avant de continuer.






Chapitre précédent     Sommaire     Chapitre suivant


La résolution des liens


Commençons en douceur avec un peu d'héritage tout simple. Vous en avez marre de notre RPG ? Moi aussi. Prenons un autre exemple pour varier un peu. Attaquons donc la création d'un programme de gestion d'un garage et des véhicules qui y sont stationnés. Imaginons que notre fier garagiste sache réparer à la fois des voitures et des motos.
Dans son programme, il aurait les classes suivantes : Vehicule, Voiture et Moto.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Vehicule
{
   public:
   void affiche() const;  //Affiche une description du Vehicule
   protected:
   int m_prix;  //Chaque vhicule a un prix
};
class Voiture : public Vehicule //Une Voiture EST UN Vehicule
{
   public:
   void affiche() const;
   private:
   int m_portes;  //Le nombre de portes de la voiture
};
class Moto : public Vehicule  //Une Moto EST UN Vehicule
{
   public:
   void affiche() const;
 
   private:
   double m_vitesse;  //La vitesse maximale de la moto
};

L'exemple est bien sûr simplifié au maximum : il manque beaucoup de méthodes, d'attributs ainsi que les constructeurs. Je vous laisse compléter selon vos envies.
Le corps des fonctions affiche() est le suivant :
1
2
3
4
5
6
7
8
9
10
11
12
void Vehicule::affiche() const
{
   cout << "Ceci est un vehicule." << endl;
}
void Voiture::affiche() const
{
   cout << "Ceci est une voiture." << endl;
}
void Moto::affiche() const
{
   cout << "Ceci est une moto." << endl;
}

Chaque classe affiche donc un message différent. Et si vous avez bien suivi le chapitre précédent, vous aurez remarqué que j'utilise ici le masquage pour redéfinir la fonction affiche() de Vehicule dans les deux classes filles.

Essayons donc ces fonctions avec un petit main() tout bête :
1
2
3
4
5
6
7
8
int main()
{
   Vehicule v;
   v.affiche();    //Affiche "Ceci est un vehicule."
   Moto m;
   m.affiche();    //Affiche "Ceci est une moto."
   return 0;
}

Je vous invite à tester, vous ne devriez rien observer de particulier. Mais cela va venir.

La résolution statique des liens



Créons une fonction supplémentaire qui reçoit en paramètre un Vehicule et modifions le main() afin d'utiliser cette fonction :
1
2
3
4
5
6
7
8
9
10
11
12
void presenter(Vehicule v)  //Prsente le vhicule pass en argument
{
   v.affiche();
}
int main()
{
   Vehicule v;
   presenter(v);
   Moto m;
   presenter(m);
   return 0;
}

A priori, rien n'a changé. Les messages affichés devraient être les mêmes. Voyons cela :
Ceci est un vehicule.
Ceci est un vehicule.

Le message n'est pas correct pour la moto ! C'est comme si, lors du passage dans la fonction, la vraie nature de la moto s'était perdue et qu'elle était redevenue un simple véhicule.

Comment est-ce possible ?


Comme il y a une relation d'héritage, nous savons qu'une moto est un véhicule, un véhicule amélioré en quelque sorte puisqu'il possède un attribut supplémentaire. La fonction presenter() reçoit en argument un Vehicule. Ce peut être un objet réellement de type Vehicule mais aussi une Voiture ou, comme dans l'exemple, une Moto. Souvenez-vous de la dérivation de type introduite au chapitre précédent.
Ce qui est important c'est que, pour le compilateur, à l'intérieur de la fonction, on manipule un Vehicule. Peu importe sa vraie nature. Il va donc appeler la « version Vehicule » de la méthode afficher() et pas la « version Moto » comme on aurait pu l'espérer.
Dans l'exemple du chapitre précédent, c'est la bonne version qui était appelée puisque, à l'intérieur de la fonction, le compilateur savait s'il avait affaire à un simple personnage ou à un guerrier. Ici, dans la fonction presenter(), pas moyen de savoir ce que sont réellement les véhicules reçus en argument.

En termes techniques, on parle de résolution statique des liens. La fonction reçoit un Vehicule, c'est donc toujours la « version Vehicule » des méthodes qui sera utilisée.

C'est le type de la variable qui détermine quelle fonction membre appeler et non sa vraie nature.

Mais vous vous doutez bien que, si je vous parle de tout cela, c'est qu'il y a un moyen de changer ce comportement.

La résolution dynamique des liens



Ce qu'on aimerait, c'est que la fonction presenter() appelle la bonne version de la méthode. C'est-à-dire qu'il faut que la fonction connaisse la vraie nature du Vehicule. C'est ce qu'on appelle la résolution dynamique des liens. Lors de l'exécution, le programme utilise la bonne version des méthodes car il sait si l'objet est de type mère ou de type fille.

Pour faire cela, il faut deux ingrédients :
  • utiliser un pointeur ou une référence ;
  • utiliser des méthodes virtuelles.

Si ces deux ingrédients ne sont pas réunis, alors on retombe dans le premier cas et l'ordinateur n'a aucun moyen d'appeler la bonne méthode.


Les fonctions virtuelles


Je vous ai donné la liste des ingrédients, allons-y pour la préparation du menu. Commençons par les méthodes virtuelles.

Déclarer une méthode virtuelle



Cela a l'air effrayant en le lisant mais c'est très simple. Il suffit d'ajouter le mot-clé virtual dans le prototype de la classe (dans le fichier .h donc). Pour notre garage, cela donne :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Vehicule
{
   public:
   virtual void affiche() const;  //Affiche une description du Vehicule
   protected:
   int m_prix;  //Chaque vhicule a un prix
};
class Voiture: public Vehicule  //Une Voiture EST UN Vehicule
{
   public:
   virtual void affiche() const;
   private:
   int m_portes;  //Le nombre de portes de la voiture
};
class Moto : public Vehicule  //Une Moto EST UN Vehicule
{
   public:
   virtual void affiche() const;
 
   private:
   double m_vitesse;  //La vitesse maximale de la moto
};

Il n'est pas nécessaire de mettre « virtual » devant les méthodes des classes filles. Elles sont automatiquement virtuelles par héritage.
Personnellement, je préfère le mettre pour me souvenir de leur particularité.


Jusque là, rien de bien difficile. Notez bien qu'il n'est pas nécessaire que toutes les méthodes soient virtuelles. Une classe peut très bien proposer des fonctions « normales » et d'autres virtuelles.

Il ne faut pas mettre virtual dans le fichier .cpp mais uniquement dans le .h. Si vous essayez, votre compilateur se vengera en vous insultant copieusement !


et utiliser une référence



Le deuxième ingrédient est un pointeur ou une référence. Vous êtes certainement comme moi, vous préférez la simplicité et, par conséquent, les références. On ne va quand même pas s'embêter avec des pointeurs juste pour le plaisir.
Réécrivons donc la fonction presenter() avec comme argument une référence.
1
2
3
4
5
6
7
8
9
10
11
12
void presenter(Vehicule const& v)  //Prsente le vhicule pass en argument
{
   v.affiche();
}
int main()  //Rien n'a chang dans le main()
{
   Vehicule v;
   presenter(v);
   Moto m;
   presenter(m);
   return 0;
}

J'ai aussi ajouté un const. Comme on ne modifie pas l'objet dans la fonction, autant le faire savoir au compilateur et au programmeur en déclarant la référence constante.


Voilà. Il ne nous reste plus qu'à tester :
Ceci est un vehicule.
Ceci est une moto.

Cela marche ! La fonction presenter() a bien appelé la bonne version de la méthode. En utilisant des fonctions virtuelles ainsi qu'une référence sur l'objet, la fonction presenter() a pu correctement choisir la méthode à appeler.

On aurait obtenu le même comportement avec des pointeurs à la place des références, comme sur le schéma suivante.


Un même morceau de code a eu deux comportements différents suivant le type passé en argument. C'est donc du polymorphisme. On dit aussi que les méthodes affiche() ont un comportement polymorphique.


Les méthodes spéciales


Bon, assez parlé. À mon tour de vous poser une petite question de théorie :

Quelles sont les méthodes d'une classe qui ne sont jamais héritées ?


La réponse est simple :
  • tous les constructeurs ;
  • le destructeur.

Vous aviez trouvé ? C'est bien. Toutes les autres méthodes peuvent être héritées et peuvent avoir un comportement polymorphique si on le souhaite. Mais qu'en est-il pour ces méthodes spéciales ?

Le cas des constructeurs



Un constructeur virtuel a-t-il du sens ? Non ! Quand je veux construire un véhicule quelconque, je sais lequel je veux construire. Je peux donc à la compilation déjà savoir quel véhicule construire. Je n'ai pas besoin de résolution dynamique des liens et, par conséquent, pas besoin de virtualité. Un constructeur ne peut pas être virtuel.

Et cela va même plus loin. Quand je suis dans le constructeur, je sais quel type je construis, je n'ai donc à nouveau pas besoin de résolution dynamique des liens. D'où la règle suivante : on ne peut pas appeler de méthode virtuelle dans un constructeur. Si on essaye quand même, la résolution dynamique des liens ne se fait pas.

Le cas du destructeur



Ici, c'est un petit peu plus compliqué malheureusement.

Créons un petit programme utilisant nos véhicules et des pointeurs, puisque c'est un des ingrédients du polymorphisme.
1
2
3
4
5
6
7
8
9
int main()
{
   Vehicule *v(0);
   v = new Voiture;
   //On cre une Voiture et on met son adresse dans un pointeur de Vehicule
   v->affiche();  //On affiche "Ceci est une voiture."
   delete v;      //Et on dtruit la voiture
   return 0;
}

Nous avons un pointeur et une méthode virtuelle. La ligne v->affiche() affiche donc le message que l'on souhaitait. Le problème de ce programme se situe au moment du delete. Nous avons un pointeur mais la méthode appelée n'est pas virtuelle. C'est donc le destructeur de Vehicule qui est appelé et pas celui de Voiture !
Dans ce cas, cela ne porte pas vraiment à conséquence, le programme ne plante pas. Mais imaginez que vous deviez écrire une classe pour le maniement des moteurs électriques d'un robot. Si c'est le mauvais destructeur qui est appelé, vos moteurs ne s'arrêteront peut-être pas. Cela peut vite devenir dramatique.

Il faut donc impérativement appeler le bon destructeur. Et pour ce faire, une seule solution : rendre le destructeur virtuel ! Cela nous permet de formuler une nouvelle règle importante : un destructeur doit toujours être virtuel si on utilise le polymorphisme.

Le code amélioré



Ajoutons donc des constructeurs et des destructeurs à nos classes. Tout sera alors correct.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Vehicule
{
   public:
   Vehicule(int prix);           //Construit un vhicule d'un certain prix
   virtual void affiche() const;
   virtual ~Vehicule();          //Remarquez le 'virtual' ici
   protected:
   int m_prix;
};
class Voiture: public Vehicule
{
   public:
   Voiture(int prix, int portes);
   //Construit une voiture dont on fournit le prix et le nombre de portes
   virtual void affiche() const;
   virtual ~Voiture();
   private:
   int m_portes;
};
class Moto : public Vehicule
{
   public:
   Moto(int prix, double vitesseMax);
   //Construit une moto d'un prix donn et ayant une certaine vitesse maximale
   virtual void affiche() const;
   virtual ~Moto();
 
   private:
   double m_vitesse;
};

Il faut bien sûr également compléter le fichier source :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Vehicule::Vehicule(int prix)
   :m_prix(prix)
{}
void Vehicule::affiche() const
//J'en profite pour modifier un peu les fonctions d'affichage
{
   cout << "Ceci est un vehicule coutant " << m_prix << " euros." << endl;
}
Vehicule::~Vehicule() //Mme si le destructeur ne fait rien, on doit le mettre !
{}
Voiture::Voiture(int prix, int portes)
   :Vehicule(prix), m_portes(portes)  
{}
void Voiture::affiche() const
{
   cout << "Ceci est une voiture avec " << m_portes << " portes et coutant " << m_prix << " euros." << endl;
}
Voiture::~Voiture()
{}
Moto::Moto(int prix, double vitesseMax)
   :Vehicule(prix), m_vitesse(vitesseMax)
{}
void Moto::affiche() const
{
   cout << "Ceci est une moto allant a " << m_vitesse << " km/h et coutant " << m_prix << " euros." << endl;
}
Moto::~Moto()
{}

Nous sommes donc prêts à aborder un exemple concret d'utilisation du polymorphisme. Attachez vos ceintures !


Les collections hétérogènes


Je vous ai dit tout au début du chapitre que nous voulions créer un programme de gestion d'un garage. Par conséquent, nous allons devoir gérer une collection de voitures et de motos. Nous ferons donc appel à des tableaux dynamiques !
1
2
vector<Voiture> listeVoitures;
vector<Moto> listeMotos;

Bien ! Mais pas optimal. Si notre ami garagiste commence à recevoir des commandes pour des scooters, des camions, des fourgons, des vélos, etc. il va falloir déclarer beaucoup de vectors. Cela veut dire qu'il va falloir apporter de grosses modifications au code à chaque apparition d'un nouveau type de véhicule.

Le retour des pointeurs



Il serait bien plus judicieux de mettre le tout dans un seul tableau ! Comme les motos et les voitures sont des véhicules, on peut déclarer un tableau de véhicules et mettre des motos dedans.
Mais si nous procédons ainsi, nous allons alors perdre la vraie nature des objets. Souvenez-vous des deux ingrédients du polymorphisme ! Il nous faut donc un tableau de pointeurs ou un tableau de références. On ne peut pas créer un tableau de références (rappelez-vous, les références ne sont que des étiquettes), nous allons donc devoir utiliser des pointeurs.

Vous vous rappelez du chapitre sur les pointeurs ? Je vous avais présenté trois cas d'utilisations. En voici donc un quatrième. J'espère que vous ne m'en voulez pas trop de ne pas en avoir parlé avant
1
2
3
4
5
int main()
{
   vector<Vehicule*> listeVehicules;
   return 0;
}

C'est ce qu'on appelle une collection hétérogène puisqu'elle contient, d'une certaine manière, des types différents.

Utiliser la collection



Commençons par remplir notre tableau. Comme nous allons accéder à nos véhicules uniquement via les pointeurs, nous n'avons pas besoin d'étiquettes sur nos objets et nous pouvons utiliser l'allocation dynamique pour les créer. En plus, cela nous permet d'avoir directement un pointeur à mettre dans notre vector.
1
2
3
4
5
6
7
8
9
10
11
12
13
int main()
{
   vector<Vehicule*> listeVehicules;
   listeVehicules.push_back(new Voiture(15000, 5));
   //J'ajoute ma collection de vhicules une voiture
   //Valant 15000 euros et ayant 5 portes
   listeVehicules.push_back(new Voiture(12000, 3));  //...
   listeVehicules.push_back(new Moto(2000, 212.5));
   //Une moto 2000 euros allant 212.5 km/h
   //On utilise les voitures et les motos
 
   return 0;
}

La figure suivante représente notre tableau.


Les voitures et motos ne sont pas réellement dans les cases. Ce sont des pointeurs. Mais en suivant les flèches, on accède aux véhicules.

Bien ! Mais nous venons de faire une grosse faute ! Chaque fois que l'on utilise new, il faut utiliser delete pour vider la mémoire. Nous allons donc devoir faire appel à une boucle pour libérer la mémoire allouée.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main()
{
   vector<Vehicule*> listeVehicules;
   listeVehicules.push_back(new Voiture(15000, 5));
   listeVehicules.push_back(new Voiture(12000, 3));
   listeVehicules.push_back(new Moto(2000, 212.5));  
   //On utilise les voitures et les motos
 
   for(int i(0); i<listeVehicules.size(); ++i)
   {
       delete listeVehicules[i];  //On libre la i-me case mmoire alloue
       listeVehicules[i] = 0;  //On met le pointeur 0 pour viter les soucis
   }
   return 0;
}

Il ne nous reste plus qu'à utiliser nos objets. Comme c'est un exemple basique, ils ne savent faire qu'une seule chose : afficher des informations. Mais essayons quand même !
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int main()
{
   vector<Vehicule*> listeVehicules;
   listeVehicules.push_back(new Voiture(15000, 5));
   listeVehicules.push_back(new Voiture(12000, 3));
   listeVehicules.push_back(new Moto(2000, 212.5));  
   listeVehicules[0]->affiche();
   //On affiche les informations de la premire voiture
   
   listeVehicules[2]->affiche();
   //Et celles de la moto
 
   for(int i(0); i<listeVehicules.size(); ++i)
   {
       delete listeVehicules[i];  //On libre la i-me case mmoire alloue
       listeVehicules[i] = 0;  //On met le pointeur 0 pour viter les soucis
   }
   return 0;
}

Je vous invite, comme toujours, à tester. Voici ce que vous devriez obtenir :
Ceci est une voiture avec 5 portes valant 15000 euros.
Ceci est une moto allant a 212.5 km/h et valant 2000 euros.

Ce sont les bonnes versions des méthodes qui sont appelées ! Cela ne devrait pas être une surprise à ce stade. Nous avons des pointeurs (ingrédient 1) et des méthodes virtuelles (ingrédient 2).

Je vous propose d'améliorer un peu ce code en ajoutant les éléments suivants :

  • Une classe Camion qui aura comme attribut le poids qu'il peut transporter.

  • Un attribut représentant l'année de fabrication du véhicule. Ajoutez aussi des méthodes pour afficher cette information.

  • Une classe Garage qui aura comme attribut le vector<Vehicule*> et proposerait des méthodes pour ajouter/supprimer des véhicules ou pour afficher des informations sur tous les éléments contenus.

  • Une méthode nbrRoues() qui renvoie le nombre de roues des différents véhicules.

Après ce léger entraînement, terminons ce chapitre avec une évolution de notre petit programme.


Les fonctions virtuelles pures


Avez-vous essayé de programmer la méthode nbrRoues() du mini-exercice ? Si ce n'est pas le cas, il est encore temps de le faire. Elle va beaucoup nous intéresser dans la suite.
Le problème des roues

Comme c'est un peu répétitif, je vous donne ma version de la fonction pour les classes Vehicule et Voiture uniquement.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Vehicule
{
   public:
   Vehicule(int prix);          
   virtual void affiche() const;
   virtual int nbrRoues() const;  //Affiche le nombre de roues du vhicule
   virtual ~Vehicule();        
   protected:
   int m_prix;
};
class Voiture : public Vehicule
{
   public:
   Voiture(int prix, int portes);
   virtual void affiche() const;
   virtual int nbrRoues() const;  //Affiche le nombre de roues de la voiture
   virtual ~Voiture();
   private:
   int m_portes;
};

Du côté du .h, pas de souci. C'est le corps des fonctions qui risque de poser problème.
1
2
3
4
5
6
7
8
int Vehicule::nbrRoues() const
{  
 //Que mettre ici ????
}
int Voiture::nbrRoues() const
{
   return 4;
}

Vous l'aurez compris, on ne sait pas vraiment quoi mettre dans la « version Vehicule » de la méthode. Les voitures ont 4 roues et les motos 2 mais, pour un véhicule en général, on ne peut rien dire ! On aimerait bien ne rien mettre ici ou carrément supprimer la fonction puisqu'elle n'a pas de sens.
Mais si on ne déclare pas la fonction dans la classe mère, alors on ne pourra pas l'utiliser depuis notre collection hétérogène. Il nous faut donc la garder ou au minimum dire qu'elle existe mais qu'on n'a pas le droit de l'utiliser. On souhaiterait ainsi dire au compilateur : « Dans toutes les classes filles de Vehicule, il y a une fonction nommée nbrRoues() qui renvoie un int et qui ne prend aucun argument mais, dans la classe Vehicule, cette fonction n'existe pas. »

C'est ce qu'on appelle une méthode virtuelle pure.

Pour déclarer une telle méthode, rien de plus simple. Il suffit d'ajouter « = 0 » à la fin du prototype.
1
2
3
4
5
6
7
8
9
10
class Vehicule
{
   public:
   Vehicule(int prix);          
   virtual void affiche() const;
   virtual int nbrRoues() const = 0;  //Affiche le nombre de roues du vhicule
   virtual ~Vehicule();        
   protected:
   int m_prix;
};

Et évidemment, on n'a rien à écrire dans le .cpp puisque, justement, on ne sait pas quoi y mettre. On peut carrément supprimer complètement la méthode. L'important étant que son prototype soit présent dans le .h.

Les classes abstraites



Une classe qui possède au moins une méthode virtuelle pure est une classe abstraite. Notre classe Vehicule est donc une classe abstraite.

Pourquoi donner un nom spécial à ces classes ? Eh bien parce qu'elles ont une règle bien particulière : on ne peut pas créer d'objet à partir d'une classe abstraite.

Oui, oui, vous avez bien lu ! La ligne suivante ne compilera pas.
1
Vehicule v(10000); //Cration d'un vhicule valant 10000 euros.

Dans le jargon des programmeurs, on dit qu'on ne peut pas créer d'instance d'une classe abstraite.
La raison en est simple : si je pouvais créer un Vehicule, alors je pourrais essayer d'appeler la fonction nbrRoues() qui n'a pas de corps et ceci n'est pas possible.
Par contre, je peux tout à fait écrire le code suivant :
1
2
3
4
5
6
7
8
9
10
11
12
13
int main()
{
   Vehicule* ptr(0);  //Un pointeur sur un vhicule
   
   Voiture caisse(20000,5);
   //On cre une voiture
   //Ceci est autoris puisque toutes les fonctions ont un corps
   
   ptr = &caisse;  //On fait pointer le pointeur sur la voiture
   cout << ptr->nbrRoues() << endl;
   //Dans la classe fille, nbrRoues() existe, ceci est donc autoris
   return 0;
}

Ici, l'appel à la méthode nbrRoues() est polymorphique puisque nous avons un pointeur et que notre méthode est virtuelle. C'est donc la « version Voiture » qui est appelée. Donc même si la « version Vehicule » n'existe pas, il n'y a pas de problèmes.

Si l'on veut créer une nouvelle sorte de Vehicule (Camion par exemple), on sera obligé de redéfinir la fonction nbrRoues(), sinon cette dernière sera virtuelle pure par héritage et, par conséquent, la classe sera abstraite elle aussi.

On peut résumer les fonctions virtuelles de la manière suivante :
  • une méthode virtuelle peut être redéfinie dans une classe fille ;
  • une méthode virtuelle pure doit être redéfinie dans une classe fille.

Dans la bibliothèque Qt, que nous allons très bientôt aborder, il y a beaucoup de classes abstraites. Il existe par exemple une classe par sorte de bouton, c'est-à-dire une classe pour les boutons normaux, une pour les cases à cocher, etc. Toutes ces classes héritent d'une classe nommée QAbstractButton, qui regroupe des propriétés communes à tous les boutons (taille, texte, etc.). Mais comme on ne veut pas autoriser les utilisateurs à mettre des QAbstractButtonsur leurs fenêtres, les créateurs de la bibliothèque ont rendu cette classe abstraite.




En résumé



  • Le polymorphisme permet de manipuler des objets d'une classe fille via des pointeurs ou des références sur une classe mère.

  • Deux ingrédients sont nécessaires : des fonctions virtuelles et des pointeurs ou références sur l'objet.

  • Si l'on ne sait pas quoi mettre dans le corps d'une méthode de la classe mère, on peut la déclarer virtuelle pure.

  • Une classe avec des méthodes virtuelles pures est dite abstraite. On ne peut pas créer d'objet à partir d'une telle classe.



Chapitre précédent     Sommaire     Chapitre suivant



Distribué et adapté par David
Consulté 2238 fois



Hébergeur du site : David
Version PHP : 5.4.45-0+deb7u2
Uptime : 354 jours 20 heures 15 minutes
Espace libre : 1514 Mo
Dernière sauvegarde : 13/11/2019
Taille de la sauvegarde : 1115 Mo


5644776 pages ont été consultées sur le site !
Dont 3223 pages pendant les 24 dernières heures.

Page générée en 0.654 secondes


Nos sites préférés
- Création d'un jeu de plateforme de A à Z avec SDL
- Zelda ROTH : Jeux amateurs sur le thème de Zelda
- Zeste de Savoir : la connaissance pour tous et sans pépins
- YunoHost : s'héberger soi-même en toute simplicité
- Site de Fvirtman : recueil de projets et de codes en C et C++
- Par ici la sortie : le site des idées de sorties


  © 2005-2019 linor.fr - Toute reproduction totale ou partielle du contenu de ce site est strictement interdite.