C'est quoi le polymorphisme ? concrètement ?
Ça n'est pas exclusif à C++, tout langage objet bien conçu permet de faire du polymorphisme.
L'idée, c'est de permettre la manipulation d'objets de classes différentes sous le nom d'une seule classe ou interface (dont héritent toutes les classes concernées), de manière à ce qu'un même appel de méthode puisse appeler des bouts de code différents suivant l'objet choisi (pourtant vu sous la même classe que les autres). Bon, j'imagine que dit comme ça c'est vite le bordel dans ta tête et ça se comprend...
En gros, le polymorphisme s'applique à l'aide d'héritage et de méthodes virtuelles en C++.
Tu définis une classe mère A, avec une méthode affiche() par exemple, que du déclares virtuelle. Au passage tu peux (mais rien ne t'y oblige) fournir une implémentation de la méthode affiche au sein de la classe A.
Et si tu définis des classes qui héritent de A, et implémentent elles aussi la méthode affiche() (on parle alors de redéfinition, car affiche() est déjà présente plus haut dans l'arbre d'héritage), le caractère virtuel de la méthode affiche() dans la classe A devient intéressant:
tu peux déclarer un pointeur de type A* et le faire, en réalité, pointer sur un objet de classe B, ou C, ou D... tant que ces classes héritent de A. Mais si tu appelles affiche() sur ce pointeur de type A*, et que l'objet effectif qui est derrière est de classe B par exemple, pour peu que la classe B redéfinisse affiche(), c'est l'implémentation présente dans B qui sera appelée au lieu de celle de A comme on pourrait s'y attendre. De même pour des objets de classe C ou D, si on redéfinit affiche() dans ces dernières.
Et ça peut aller beaucoup plus loin que ça, mais en gros pour résumer ça fait intervenir des mécanismes qui permettent de ne décider du code qui sera appelé qu'à l'exécution (ce qui est assez intéressant).
Si tu manipules une liste de pointeurs A*, tu n'as plus à te soucier des types effectifs des objets pointés, les différents bouts de code correspondants seront appelés quand tu demanderas affiche() (ou l'implémentation présente dans A s'il y en a une, dans le cas où le type effectif pointé ne la redéfinit pas).
Le plus simple pour comprendre le polymorphisme, c'est encore de prendre un exemple. Si tu veux créer une fonction d'addition de 2 nombres, il serait intéressant d'en créer une seule quel que soit leur type (entier, réels, etc...), puisque le calcul est le même. Au risque de me trouver en C++, je donne l'exemple en Java :
Dans le cas d'entiers, tu écriras :
public int add(int a, int b)
{
return a + b;
}
Et dans le cas de réels, tu écriras :
public double add(double a, double b)
{
return a + b;
}
La seule chose qui diffère dans ta fonction d'addition, c'est le type de données passé en paramètre et le type de retour, le contenu de la fonction est le même. Tu rends abstraite ta fonction en s'adaptant au type de données, d'où "polymorphisme".
De peur que tu ne t'embrouilles avec des explications différentes que celles de mon VDD, je tiens à préciser qu'il existe 3 notions de polymorphisme :
- ad hoc (surcharge)
- paramétrique (généricité)
- héritage (redéfinition)
Mon explication concerne celle sur le polymorphisme paramétrique, mon VDD sans doute une autre. Si tu veux plus d'informations :
http://www.commentcamarche.net/contents/811-poo-le-polymorphisme
Du coup j'en viens à me demander si ton exemple ne relève pas du polymorphisme "ad hoc", dans la mesure où il s'agit ici de surcharges justement.
Ce que tu nommes le polymorphisme paramétrique, ça ne serait pas plutôt implémenté sous forme de templates (C++) ou de generics (Java / C#)? Car effectivement dans le cas des generics / templates, on écrit un seul algo pour une infinité de types possibles (en partant du principe qu'ils respectent certaines contraintes, ici, la compatibilité par l'opérateur + avec un opérande de l'autre type utilisé).
Sinon, c'est marrant parce qu'à mes yeux la surcharge ne constitue pas vraiment un cas de polymorphisme. Il y a bien plusieurs comportements cachés sous un même nom... mais ces comportements sont appelés de manière très prévisible en fin de compte, je vois plus ça comme un petit luxe qui évite d'avoir à utiliser 5 noms différents pour une même fonction quand on veut qu'elle sache travailler avec différents types d'arguments. ![]()
Les adeptes de programmation fonctionnelle qui meurent à petit feu en lisant ce topic
"Du coup j'en viens à me demander si ton exemple ne relève pas du polymorphisme "ad hoc", dans la mesure où il s'agit ici de surcharges justement.
"
Le polymorphisme de type surcharge consiste à créer plusieurs méthodes portant le même nom mais qui diffèrent de leur paramètre, c'est un peu différent. Dans le cas du polymorphisme paramétrique, c'est une seule méthode qui peut être utilisée dans des cas différents en fonction du type de paramètres.
"Ce que tu nommes le polymorphisme paramétrique, ça ne serait pas plutôt implémenté sous forme de templates (C++) ou de generics (Java / C#)?"
Oui c'est exactement ce que j'ai tenté d'expliquer avec mon exemple.
C'est en tout cas les souvenirs que j'ai lorsque j'avais commencé à apprendre le Java et qui, en faisant des recherches, ont l'air d'être corrects ![]()
Lowenheim > Quel est le rapport ? ![]()
Tu sais que beaucoup de langages fonctionnels intègrent (au moins) une forme de polymorphisme paramétrique ?
Caletlog
C'était simplement un petit troll parce que pour moi quand j'entends "polymorphisme", je pense au vrai polymorphisme des langages fonctionnels et non aux vieux hacks tels que les templates de C++, ou encore moins à la surcharge
Mais là c'est bien ce que demandait l'auteur donc ne l'embrouillons pas ![]()
Ah d'accord, j'avais compris dans l'autre sens.
En gros le polymorphisme sert à manipuler des objets via des pointeurs et ou des références ?
Je crois que je commence à comprendre ! ![]()
« je pense au vrai polymorphisme des langages fonctionnels et non aux vieux hacks tels que les templates de C++, ou encore moins à la surcharge
»
C'est pareil pour moi, à la base dans ma tête le seul polymorphisme existant c'est celui qui se base sur l'héritage et les tables de fonctions virtuelles ![]()
Les templates c'est juste une jolie syntaxe pour des macros évoluées (et le meilleur moyen de devenir fou quand on mélange ça avec de l'héritage et d'autres joyeusetés...), et la surcharge, j'ai jamais trouvé que ça procurait du polymorphisme.
«
Le polymorphisme de type surcharge consiste à créer plusieurs méthodes portant le même nom mais qui diffèrent de leur paramètre, c'est un peu différent. Dans le cas du polymorphisme paramétrique, c'est une seule méthode qui peut être utilisée dans des cas différents en fonction du type de paramètres. »
Sauf que dans ton bout de code,
public int add(int a, int b)
{
return a + b;
}
Et dans le cas de réels, tu écriras :
public double add(double a, double b)
{
return a + b;
}
on a bien plusieurs méthodes qui portent le même nom, et pas une seule méthode de type template/generic qui accepte directement des types d'arguments variables ![]()
D'où ma remarque sur "ton exemple c'est de l'ad hoc / surcharge, pas du paramétrique
" ![]()
Google_Bot : non c'est justement ce que j'ai dit dans mon premier post par "si tu veux créer une fonction d'addition de 2 nombres, il serait intéressant d'en créer une seule quel que soit leur type (entier, réels, etc...), puisque le calcul est le même."
Sans utiliser le polymorphisme paramétrique, on aurait écrit les 2 fonctions que j'ai donné pour des entiers et des rééls (et plus de 2 si autres types,), ce qui aurait été du polymorphisme de surcharge en effet. Mais avec le paramétrique, écrire ceci serait suffisant :
public T add(T a, T b)
{
return a + b;
}
où T est le type qui peut prendre forme d'entiers, réels, etc...
OK OK mais un post plus bas tu précises que ton explication "concerne le polymorphisme paramétrique", c'est ça que je trouvais bizarre dans ton raisonnement. Bref je pense qu'on s'est compris.
Academiquement parlant, on distingue deux type de polymorphismes: Le polymorphisme de type et le polymorphisme de fonction. L'un est le polymorphisme classique des langages oriente objet et l'autre est le polymorphisme des langages fonctionel.
Les deux sont disponibles en C++. Celui qui est typiquement appelle polymorphism en C++ est l'utilisation d'heritage de classe et de fonction virtual. L'autre est typiquement appelle meta programmation et utilise principalement les mecanismes template.
Je n'arrive pas a retrouver le lien qui expliquait la difference vachement bien. Mais je suis sur que chris_27 s'en rappelle. (Si quelqu'un l'a sur IRC, pinger le).
Cela etant dit, quand on parle de polymorphisme en C++, on ne parle jamais de meta-programmation. On parle toujours du mecanisme heritage/appel de fonction virtuelle.
Dans l'exemple ci-dessous:
void afficher(Vehicule v) const
{
v.fonction();
}
int main()
{
afficher(Voiture BMW); //Voiture est une sous classe de Vehicule
}
Dans la fonction afficher, un argument de type Vehicule est attendu. Mais quand je passe en argument Voiture dans la fonction afficher j'ai une érreur.
Pouvez vous m'éclairer sur ce problème s'il vous plaît ? ![]()
donne la definition de tes classes.
En faite, si le code marche mais il fait pas sque je veux.Voici la définition des méthodes de la classe:
void Vehicule::affiche() const
{
cout << "Ceci est un vehicule." << endl;
}
void Voiture::affiche() const
{
cout << "Ceci est une voiture." << endl;
}
Voici mon main.ccp:
void afficher(Vehicule v) const
{
v.affiche();
}
int main()
{
afficher(Voiture BMW); //Voiture est une sous classe de Vehicule
}
Le truc c'est que quand je compile et j'éxécute le main.cpp, normalement je devrais avoir un méssage:
"Ceci est une voiture."
Mais j'ai "Ceci est un Vehicule"
Hors c'est une voiture que j'ai passé en argument ! ![]()
donnes tout le code.
c'est ça tout le code
Ya le .h des 2 classes mais il n'y a pas d'érreur à ce niveau là, c'est juste que le compilateur fait pas ce que je lui demande ![]()