AUTANT QUE JE SACHE:
C ++ fournit trois types différents de polymorphisme.
- Fonctions virtuelles
- Surcharge du nom de la fonction
- Surcharge de l'opérateur
En plus des trois types de polymorphisme ci-dessus, il existe d'autres types de polymorphisme:
- Durée
- au moment de la compilation
- polymorphisme ad hoc
- polymorphisme paramétrique
Je sais que le polymorphisme d'exécution peut être réalisé par des fonctions virtuelles et que le polymorphisme statique peut être réalisé par des fonctions de modèle
Mais pour les deux autres
polymorphisme ad hoc:
Si la gamme de types réels qui peuvent être utilisés est finie et que les combinaisons doivent être spécifiées individuellement avant utilisation, on parle de polymorphisme ad hoc.
polymorphisme paramétrique:
Si tout le code est écrit sans mention d'un type spécifique et peut donc être utilisé de manière transparente avec un nombre quelconque de nouveaux types, il est appelé polymorphisme paramétrique.
Je peux à peine les comprendre :(
quelqu'un peut-il expliquer les deux si possible avec un exemple? J'espère que les réponses à ces questions seront utiles pour de nombreux nouveaux évanouissements de leurs collèges.
la source
Réponses:
Compréhension / exigences du polymorphisme
Pour comprendre le polymorphisme - comme le terme est utilisé en science informatique - il est utile de partir d'un simple test et de sa définition. Considérer:
Ici,
f()
consiste à effectuer une opération et reçoit des valeursx
et desy
entrées.Mécanismes C ++ pour le polymorphisme
Polymorphisme explicite spécifié par le programmeur
Vous pouvez écrire de
f()
telle sorte qu'il puisse fonctionner sur plusieurs types de l'une des manières suivantes:Prétraitement:
Surcharge:
Modèles:
Envoi virtuel:
Autres mécanismes connexes
Le polymorphisme fourni par le compilateur pour les types intégrés, les conversions standard et la conversion / coercition sont abordés plus loin pour être complets comme suit:
Terminologie
Catégorisation plus poussée
Compte tenu des mécanismes polymorphes ci-dessus, nous pouvons les catégoriser de différentes manières:
Quand le code spécifique au type polymorphe est-il sélectionné?
f
ci-dessus avec desint
arguments - en fonction du mécanisme polymorphe utilisé et des choix d'inlining, le compilateur peut éviter de générer du codef(double)
, ou le code généré peut être jeté à un moment donné de la compilation ou de la liaison. ( tous les mécanismes ci-dessus sauf l'envoi virtuel )Quels types sont pris en charge?
Cela signifie que vous pouvez simplement essayer d'utiliser la fonction pour différents types de paramètres sans rien faire spécifiquement pour activer sa prise en charge pour eux (par exemple, des modèles, des macros). Un objet avec des fonctions / opérateurs qui agissent comme le modèle / macro attend 1 est tout ce dont le modèle / macro a besoin pour faire son travail, le type exact n'étant pas pertinent. Les «concepts» introduits par C ++ 20 expriment et imposent de telles attentes - voir la page de référence ici .
Le polymorphisme paramétrique fournit le typage du canard - un concept attribué à James Whitcomb Riley qui a apparemment dit: «Quand je vois un oiseau qui marche comme un canard et nage comme un canard et charlatan comme un canard, j'appelle cet oiseau un canard». .
Le polymorphisme de sous-type (aka inclusion) vous permet de travailler sur de nouveaux types sans mettre à jour l'algorithme / la fonction, mais ils doivent être dérivés de la même classe de base (répartition virtuelle)
1 - Les modèles sont extrêmement flexibles. SFINAE (voir aussi
std::enable_if
) permet effectivement plusieurs ensembles d'attentes pour le polymorphisme paramétrique. Par exemple, vous pouvez coder que lorsque le type de données que vous traitez a un.size()
membre, vous utiliserez une fonction, sinon une autre fonction qui n'en a pas besoin.size()
(mais qui souffre vraisemblablement d'une certaine manière - par exemple, utiliser le plus lentstrlen()
ou ne pas imprimer comme utile un message dans le journal). Vous pouvez également spécifier des comportements ad hoc lorsque le modèle est instancié avec des paramètres spécifiques, en laissant certains paramètres paramétriques ( spécialisation partielle du modèle ) ou non ( spécialisation complète )."Polymorphe"
Alf Steinbach commente que, dans le standard C ++, polymorphique se réfère uniquement au polymorphisme d'exécution utilisant la répartition virtuelle. Général Comp. Sci. la signification est plus inclusive, selon le glossaire du créateur de C ++ Bjarne Stroustrup ( http://www.stroustrup.com/glossary.html ):
Cette réponse - comme la question - relie les fonctionnalités C ++ à Comp. Sci. terminologie.
Discussion
Avec le standard C ++ en utilisant une définition plus étroite du «polymorphisme» que le Comp. Sci. communauté, pour assurer une compréhension mutuelle de votre public, pensez à ...
Pourtant, ce qui est crucial pour être un grand programmeur C ++, c'est de comprendre ce que le polymorphisme fait vraiment pour vous ...
vous permettant d'écrire du code "algorithmique" une fois, puis de l'appliquer à de nombreux types de données
... et soyez donc très conscient de la façon dont les différents mécanismes polymorphes correspondent à vos besoins réels.
Le polymorphisme d'exécution convient:
Base*
s,Lorsqu'il n'y a pas de pilote clair pour le polymorphisme au moment de l'exécution, les options de compilation sont souvent préférables. Considérer:
__FILE__
,__LINE__
, Concaténation de chaîne littérale et d' autres capacités uniques de macros (qui restent mal ;-))Autres mécanismes soutenant le polymorphisme
Comme promis, pour être complet, plusieurs sujets périphériques sont couverts:
Cette réponse se termine par une discussion sur la manière dont les éléments ci-dessus se combinent pour renforcer et simplifier le code polymorphique - en particulier le polymorphisme paramétrique (modèles et macros).
Mécanismes de mappage vers des opérations spécifiques au type
> Surcharges implicites fournies par le compilateur
Conceptuellement, le compilateur surcharge de nombreux opérateurs pour les types intégrés. Ce n'est pas conceptuellement différent de la surcharge spécifiée par l'utilisateur, mais est répertorié car elle est facilement négligée. Par exemple, vous pouvez ajouter à
int
s etdouble
s en utilisant la même notationx += 2
et le compilateur produit:La surcharge s'étend ensuite de manière transparente aux types définis par l'utilisateur:
Les surcharges fournies par le compilateur pour les types de base sont courantes dans les langages informatiques de haut niveau (3GL +), et une discussion explicite du polymorphisme implique généralement quelque chose de plus. (Les 2GL - langages d'assemblage - nécessitent souvent que le programmeur utilise explicitement différents mnémoniques pour différents types.)
> Conversions standard
La quatrième section de la norme C ++ décrit les conversions standard.
Le premier point résume bien (à partir d'un ancien brouillon - espérons-le encore substantiellement correct):
Zéro ou une conversion de l'ensemble suivant: conversion de lvaleur en rvalue, conversion de tableau en pointeur et conversion de fonction en pointeur.
Aucune ou une conversion de l'ensemble suivant: promotions intégrales, promotion en virgule flottante, conversions intégrales, conversions en virgule flottante, conversions intégrales flottantes, conversions de pointeur, conversions de pointeur vers des membres et conversions booléennes.
Aucune ou une conversion de qualification.
Ces conversions autorisent du code tel que:
Application du test précédent:
a()
lui-même exécute du code spécifiquement pourdouble
et n'est donc pas polymorphe.Mais, dans le second appel au
a()
compilateur sait générer un code de type approprié pour une « promotion de la virgule flottante » (Standard §4) pour convertir42
à42.0
. Ce code supplémentaire est dans la fonction d' appel . Nous discuterons de l'importance de cela dans la conclusion.> Coercition, casts, constructeurs implicites
Ces mécanismes permettent aux classes définies par l'utilisateur de spécifier des comportements similaires aux conversions standard des types intégrés. Regardons:
Ici, l'objet
std::cin
est évalué dans un contexte booléen, à l'aide d'un opérateur de conversion. Cela peut être regroupé conceptuellement avec "promotions intégrales" et al des conversions standard dans le sujet ci-dessus.Les constructeurs implicites font effectivement la même chose, mais sont contrôlés par le type cast-to:
Implications des surcharges, conversions et coercitions fournies par le compilateur
Considérer:
Si nous voulons que le montant
x
à traiter comme un nombre réel au cours de la division (c. -à- 6,5 plutôt que vers le bas arrondi à 6), nous ne devons changementtypedef double Amount
.C'est bien, mais cela n'aurait pas été trop de travail de rendre le code explicitement "type correct":
Mais, considérez que nous pouvons transformer la première version en un
template
:C'est grâce à ces petites "fonctionnalités pratiques" qu'il peut être si facilement instancié pour
int
oudouble
et fonctionner comme prévu. Sans ces fonctionnalités, nous aurions besoin de transtypages explicites, de traits de type et / ou de classes de règles, des désordres verbeux et sujets aux erreurs comme:Ainsi, la surcharge d'opérateurs fournie par le compilateur pour les types intégrés, les conversions standard, le casting / la coercition / les constructeurs implicites - ils contribuent tous à un support subtil du polymorphisme. À partir de la définition en haut de cette réponse, ils traitent de «trouver et exécuter du code approprié au type» en mappant:
"loin" des types de paramètres
à partir des nombreux types de données poignées de code algorithmique polymorphes
au code écrit pour un nombre (potentiellement moindre) de types (identiques ou autres).
"à" types paramétriques à partir de valeurs de type constant
Ils n'établissent pas de contextes polymorphes par eux-mêmes, mais aident à autonomiser / simplifier le code dans de tels contextes.
Vous pouvez vous sentir trompé ... cela ne semble pas beaucoup. L'importance est que dans des contextes polymorphes paramétriques (c'est-à-dire à l'intérieur de modèles ou de macros), nous essayons de prendre en charge une gamme arbitrairement large de types, mais nous voulons souvent exprimer des opérations sur eux en termes d'autres fonctions, littéraux et opérations qui ont été conçus pour un petit ensemble de types. Cela réduit la nécessité de créer des fonctions ou des données presque identiques sur une base par type lorsque l'opération / la valeur est logiquement la même. Ces fonctionnalités coopèrent pour ajouter une attitude de «meilleur effort», faisant ce que l'on attend intuitivement en utilisant les fonctions et données disponibles limitées et ne s'arrêtant avec une erreur qu'en cas d'ambiguïté réelle.
Cela permet de limiter le besoin de code polymorphique prenant en charge le code polymorphique, de dessiner un réseau plus étroit autour de l'utilisation du polymorphisme afin que l'utilisation localisée ne force pas une utilisation généralisée, et de rendre les avantages du polymorphisme disponibles au besoin sans imposer les coûts de devoir exposer l'implémentation à au moment de la compilation, ayez plusieurs copies de la même fonction logique dans le code objet pour prendre en charge les types utilisés, et en effectuant une répartition virtuelle par opposition à l'inlining ou au moins aux appels résolus au moment de la compilation. Comme c'est généralement le cas en C ++, le programmeur dispose d'une grande liberté pour contrôler les limites dans lesquelles le polymorphisme est utilisé.
la source
dynamic_cast
requiert un "pointeur vers ou une lvalue d'un type polymorphe". Cheers & hth.,En C ++, la distinction importante est la liaison à l'exécution et à la compilation. Ad-hoc vs paramétrique n'aide pas vraiment, comme je l'expliquerai plus tard.
Remarque - le polymorphisme à l'exécution peut encore être résolu au moment de la compilation, mais ce n'est qu'une optimisation. Le besoin de prendre en charge efficacement la résolution au moment de l'exécution et de faire des compromis avec d'autres problèmes fait partie de ce qui a conduit les fonctions virtuelles à ce qu'elles sont. Et c'est vraiment la clé pour toutes les formes de polymorphisme en C ++ - chacune résulte de différents ensembles de compromis effectués dans un contexte différent.
La surcharge de fonctions et la surcharge de l'opérateur sont la même chose à tous égards. Les noms et la syntaxe pour les utiliser n'affectent pas le polymorphisme.
Les modèles vous permettent de spécifier de nombreuses surcharges de fonctions à la fois.
Il y a un autre ensemble de noms pour la même idée de temps de résolution ...
Ces noms sont davantage associés à la POO, il est donc un peu étrange de dire qu'un modèle ou une autre fonction non membre utilise une liaison précoce.
Pour mieux comprendre la relation entre les fonctions virtuelles et la surcharge de fonctions, il est également utile de comprendre la différence entre «envoi unique» et «envoi multiple». L'idée peut être comprise comme une progression ...
Il y a évidemment plus dans la POO qu'une excuse pour désigner un paramètre comme spécial, mais cela en fait partie. Et pour revenir à ce que j'ai dit à propos des compromis - l'envoi unique est assez facile à faire efficacement (l'implémentation habituelle est appelée "tables virtuelles"). La répartition multiple est plus gênante, non seulement en termes d'efficacité, mais aussi pour une compilation séparée. Si vous êtes curieux, vous pouvez rechercher "le problème de l'expression".
Tout comme il est un peu étrange d'utiliser le terme «liaison précoce» pour les fonctions non membres, il est un peu étrange d'utiliser les termes «envoi unique» et «envoi multiple» où le polymorphisme est résolu au moment de la compilation. Habituellement, C ++ est considéré comme n'ayant pas de répartition multiple, ce qui est considéré comme un type particulier de résolution d'exécution. Cependant, la surcharge de fonctions peut être considérée comme une distribution multiple effectuée au moment de la compilation.
Pour en revenir au polymorphisme paramétrique ou ad hoc, ces termes sont plus populaires en programmation fonctionnelle, et ils ne fonctionnent pas tout à fait en C ++. Toutefois...
Le polymorphisme paramétrique signifie que vous avez des types comme paramètres et que le même code est utilisé quel que soit le type que vous utilisez pour ces paramètres.
Le polymorphisme ad hoc est ad hoc dans le sens où vous fournissez un code différent en fonction des types particuliers.
La surcharge et les fonctions virtuelles sont deux exemples de polymorphisme ad hoc.
Encore une fois, il y a quelques synonymes ...
Sauf que ce ne sont pas tout à fait des synonymes, bien qu'ils soient généralement traités comme s'ils l'étaient, et c'est là que la confusion risque de survenir en C ++.
Le raisonnement derrière les traiter comme des synonymes est qu'en contraignant le polymorphisme à des classes de types particulières, il devient possible d'utiliser des opérations spécifiques à ces classes de types. Le mot «classes» ici peut être interprété dans le sens de la POO, mais se réfère en réalité simplement à des ensembles (généralement nommés) de types qui partagent certaines opérations.
Ainsi, le polymorphisme paramétrique est généralement pris (au moins par défaut) comme impliquant un polymorphisme sans contrainte. Étant donné que le même code est utilisé quels que soient les paramètres de type, les seules opérations prises en charge sont celles qui fonctionnent pour tous les types. En laissant l'ensemble des types sans contrainte, vous limitez sévèrement l'ensemble des opérations que vous pouvez appliquer à ces types.
Dans par exemple Haskell, vous pouvez avoir ...
Le
a
ici est un type polymorphe sans contrainte. Cela peut être n'importe quoi, donc nous ne pouvons pas faire grand-chose avec des valeurs de ce type.Ici,
a
est contraint d'être membre de laNum
classe - types qui agissent comme des nombres. Cette contrainte vous permet de faire des choses numérotées avec ces valeurs, comme les ajouter. Même l3
'inférence de type polymorphe indique que vous voulez dire le3
typea
.Je considère cela comme un polymorphisme paramétrique contraint. Il n'y a qu'une seule implémentation, mais elle ne peut être appliquée que dans des cas contraints. L'aspect ad hoc est le choix de laquelle
+
et3
de l'utiliser. Chaque "instance" deNum
a sa propre implémentation distincte de ceux-ci. Donc même chez Haskell "paramétrique" et "sans contrainte" ne sont pas vraiment des synonymes - ne me blâmez pas, ce n'est pas ma faute!En C ++, la surcharge et les fonctions virtuelles sont un polymorphisme ad hoc. La définition du polymorphisme ad hoc ne se soucie pas de savoir si l'implémentation est sélectionnée au moment de l'exécution ou de la compilation.
C ++ est très proche du polymorphisme paramétrique avec des modèles si chaque paramètre de modèle a un type
typename
. Il existe des paramètres de type et une seule implémentation, quels que soient les types utilisés. Toutefois, la règle «L'échec de substitution n'est pas une erreur» signifie que des contraintes implicites surviennent suite à l'utilisation d'opérations dans le modèle. Les complications supplémentaires incluent la spécialisation des modèles pour fournir des modèles alternatifs - différentes implémentations (ad hoc).Donc, d'une certaine manière, le C ++ a un polymorphisme paramétrique, mais il est implicitement contraint et pourrait être remplacé par des alternatives ad hoc - c'est-à-dire que cette classification ne fonctionne pas vraiment pour C ++.
la source
a
voici un type polymorphe sans contrainte [...] donc nous ne pouvons pas faire grand-chose avec des valeurs de ce type". était intéressant - en C ++ sans Concepts, vous n'êtes pas limité à tenter uniquement un ensemble spécifique d'opérations sur un argument d'un type spécifié comme paramètre de modèle ... les bibliothèques comme les concepts boost fonctionnent dans l'autre sens - en vous assurant que le type prend en charge les opérations vous spécifiez, plutôt que de vous prémunir contre l'utilisation accidentelle d'opérations supplémentaires.Quant au polymorphisme ad hoc, cela signifie une surcharge de fonction ou une surcharge d'opérateur. Découvrez ici:
http://en.wikipedia.org/wiki/Ad-hoc_polymorphism
En ce qui concerne le polymorphisme paramétrique, les fonctions de modèle peuvent également être prises en compte car elles ne prennent pas nécessairement des paramètres de types FIXED. Par exemple, une fonction peut trier un tableau d'entiers et elle peut également trier un tableau de chaînes, etc.
http://en.wikipedia.org/wiki/Parametric_polymorphism
la source
<
opérateurs et similaires). Dans Haskell, vous exprimeriez cette exigence explicitement en utilisant la classeOrd
. Le fait que vous obteniez un différent en<
fonction du type particulier (tel que fourni par l'instance deOrd
) serait considéré comme un polymorphisme ad hoc.Cela n'a peut-être pas été utile, mais j'ai fait cela pour initier mes amis à la programmation en donnant des fonctions définies, comme
START
, etEND
pour la fonction principale, donc ce n'était pas trop intimidant (ils n'utilisaient que le fichier main.cpp ). Il contient des classes et des structures polymorphes, des modèles, des vecteurs, des tableaux, des directives de préprocesseur, une amitié, des opérateurs et des pointeurs (que vous devriez probablement savoir avant de tenter le polymorphisme):Remarque: ce n'est pas fini, mais vous pouvez vous faire une idée
main.cpp
main.h
la source
Voici un exemple de base utilisant des classes polymorphes
la source
Le polymorphisme signifie de nombreuses formes en tant que tel, il est utilisé pour qu'un opérateur agisse différemment sous différentes instances. Le polymorphisme est utilisé pour implémenter l'héritage. Par exemple, nous avons défini un fn draw () pour une forme de classe, puis le draw fn peut être implémenté pour dessiner un cercle, une boîte, un triangle et d'autres formes. (qui sont des objets de la forme de classe)
la source
Si quelqu'un dit CUT à ces gens
Que va-t-il se passer?
Donc, la représentation ci-dessus montre ce qu'est le polymorphisme (même nom, comportement différent) en POO.
Si vous allez pour une interview et que l'intervieweur vous demande de dire / montrer un exemple en direct de polymorphisme dans la même pièce où nous sommes assis, dites-
Réponse - Porte / Fenêtres
Vous vous demandez comment?
Par porte / fenêtre - une personne peut venir, l'air peut venir, la lumière peut venir, la pluie peut venir, etc.
c'est-à-dire une forme de comportement différent (polymorphisme).
Pour mieux le comprendre et de manière simple, j'ai utilisé l'exemple ci-dessus. Si vous avez besoin de référence pour le code, suivez les réponses ci-dessus.
la source