HASTA DONDE SE:
C ++ proporciona tres tipos diferentes de polimorfismo.
- Funciones virtuales
- Nombre de función sobrecarga
- Sobrecarga del operador
Además de los tres tipos anteriores de polimorfismo, existen otros tipos de polimorfismo:
- tiempo de ejecución
- tiempo de compilación
- polimorfismo ad-hoc
- polimorfismo paramétrico
Sé que el polimorfismo de tiempo de ejecución se puede lograr mediante funciones virtuales y el polimorfismo estático se puede lograr mediante funciones de plantilla
Pero para los otros dos
polimorfismo ad-hoc:
Si el rango de tipos reales que se pueden usar es finito y las combinaciones deben especificarse individualmente antes de su uso, esto se llama polimorfismo ad-hoc.
polimorfismo paramétrico:
Si todo el código se escribe sin mencionar ningún tipo específico y, por lo tanto, se puede usar de forma transparente con cualquier número de nuevos tipos, se llama polimorfismo paramétrico.
Apenas puedo entenderlos :(
¿Alguien puede explicar ambos si es posible con un ejemplo? Espero que las respuestas a estas preguntas sean útiles para muchos pasajes nuevos de sus universidades.
fuente
Respuestas:
Comprensión de / requisitos para el polimorfismo
Para comprender el polimorfismo, como se usa el término en Ciencias de la Computación, es útil comenzar con una prueba simple y su definición. Considerar:
Aquí,
f()
es realizar alguna operación y se le están dando valoresx
yy
como entradas.Mecanismos C ++ para polimorfismo
Polimorfismo explícito especificado por el programador
Puedes escribir
f()
manera que pueda operar en varios tipos de cualquiera de las siguientes maneras:Preprocesamiento:
Sobrecarga:
Plantillas:
Despacho virtual:
Otros mecanismos relacionados
El polimorfismo proporcionado por el compilador para los tipos incorporados, las conversiones estándar y la conversión / coerción se analizan más adelante para completarlo como:
Terminología
Categorización adicional
Dados los mecanismos polimórficos anteriores, podemos clasificarlos de varias maneras:
¿Cuándo se selecciona el código específico de tipo polimórfico?
f
anteriormente conint
argumentos, dependiendo del mecanismo polimórfico utilizado y de las opciones en línea que el compilador podría evitar generar cualquier códigof(double)
, o el código generado podría desecharse en algún momento de la compilación o vinculación. ( todos los mecanismos anteriores excepto el despacho virtual )¿Qué tipos son compatibles?
Es decir, paramétrico , puede intentar usar la función para varios tipos de parámetros sin hacer nada específicamente para habilitar su compatibilidad (por ejemplo, plantillas, macros). Un objeto con funciones / operadores que actúan como la plantilla / macro espera 1 es todo lo que la plantilla / macro necesita para hacer su trabajo, con el tipo exacto irrelevante. Los "conceptos" introducidos por C ++ 20 expresan y hacen cumplir tales expectativas. Consulte la página de preferencias aquí .
El polimorfismo paramétrico proporciona la tipificación de patos , un concepto atribuido a James Whitcomb Riley, quien aparentemente dijo: "Cuando veo un pájaro que camina como un pato y nada como un pato y grazna como un pato, llamo a ese pájaro un pato". .
El polimorfismo de subtipo (también conocido como inclusión) le permite trabajar en nuevos tipos sin actualizar el algoritmo / función, pero deben derivarse de la misma clase base (despacho virtual)
1 - Las plantillas son extremadamente flexibles. SFINAE (ver también
std::enable_if
) efectivamente permite varios conjuntos de expectativas para el polimorfismo paramétrico. Por ejemplo, puede codificar que cuando el tipo de datos que está procesando tiene un.size()
miembro, usará una función, de lo contrario, otra función que no necesita.size()
(pero presumiblemente sufre de alguna manera, por ejemplo, usar lastrlen()
impresión más lenta o no útil un mensaje en el registro). También puede especificar comportamientos ad-hoc cuando la plantilla se instancia con parámetros específicos, ya sea dejando algunos parámetros paramétricos ( especialización parcial de plantilla ) o no ( especialización completa )."Polimórfico"
Alf Steinbach comenta que en el polimórfico estándar de C ++ solo se refiere al polimorfismo en tiempo de ejecución mediante el envío virtual. General Comp. Sci. el significado es más inclusivo, según el glosario del creador de C ++, Bjarne Stroustrup ( http://www.stroustrup.com/glossary.html ):
Esta respuesta, como la pregunta, relaciona las características de C ++ con el Comp. Sci. terminología.
Discusión
Con el estándar C ++ que utiliza una definición más estrecha de "polimorfismo" que el Comp. Sci. comunidad, para garantizar la comprensión mutua de su audiencia, considere ...
Aún así, lo que es crucial para ser un gran programador de C ++ es entender lo que el polimorfismo realmente está haciendo por usted ...
permitiéndole escribir código "algorítmico" una vez y luego aplicarlo a muchos tipos de datos
... y luego sea muy consciente de cómo los diferentes mecanismos polimórficos satisfacen sus necesidades reales.
Trajes de polimorfismo en tiempo de ejecución:
Base*
s,Cuando no hay un controlador claro para el polimorfismo en tiempo de ejecución, a menudo son preferibles las opciones de tiempo de compilación. Considerar:
__FILE__
,__LINE__
concatenación literal de cadenas y otras capacidades únicas de macros (que siguen siendo malvadas ;-))Otros mecanismos que apoyan el polimorfismo
Según lo prometido, para completar, se cubren varios temas periféricos:
Esta respuesta concluye con una discusión sobre cómo se combina lo anterior para potenciar y simplificar el código polimórfico, especialmente el polimorfismo paramétrico (plantillas y macros).
Mecanismos para mapear operaciones específicas de tipo
> Sobrecargas implícitas proporcionadas por el compilador
Conceptualmente, el compilador sobrecarga muchos operadores para los tipos incorporados. No es conceptualmente diferente de la sobrecarga especificada por el usuario, pero se enumera porque se pasa por alto fácilmente. Por ejemplo, se puede añadir a
int
s ydouble
s utilizando la misma notaciónx += 2
y el compilador produce:La sobrecarga se extiende sin problemas a los tipos definidos por el usuario:
Las sobrecargas proporcionadas por el compilador para los tipos básicos son comunes en los lenguajes informáticos de alto nivel (3GL +), y la discusión explícita del polimorfismo generalmente implica algo más. (2GLs - lenguajes de ensamblaje - a menudo requieren que el programador use explícitamente diferentes mnemónicos para diferentes tipos).
> Conversiones estándar
La cuarta sección del estándar C ++ describe las conversiones estándar.
El primer punto resume muy bien (de un borrador anterior, con suerte todavía sustancialmente correcto):
Cero o una conversión del siguiente conjunto: conversión lvalue-to-rvalue, conversión de matriz a puntero y conversión de función a puntero.
Cero o una conversión del siguiente conjunto: promociones integrales, promoción de punto flotante, conversiones integrales, conversiones de punto flotante, conversiones integrales flotantes, conversiones de puntero, conversiones de puntero a miembro y conversiones booleanas.
Cero o una conversión de calificación.
Estas conversiones permiten códigos como:
Aplicando la prueba anterior:
a()
en sí mismo ejecuta código específicamente paradouble
y, por lo tanto, no es polimórfico.Sin embargo, en la segunda convocatoria para
a()
el compilador sabe para generar el código de tipo apropiado para una "promoción de coma flotante" (Norma § 4) para convertir42
a42.0
. Ese código adicional está en la función de llamada . Discutiremos la importancia de esto en la conclusión.> Coerción, moldes, constructores implícitos
Estos mecanismos permiten que las clases definidas por el usuario especifiquen comportamientos similares a las conversiones estándar de los tipos incorporados. Echemos un vistazo:
Aquí, el objeto
std::cin
se evalúa en un contexto booleano, con la ayuda de un operador de conversión. Esto se puede agrupar conceptualmente con "promociones integrales" y otros de las conversiones estándar en el tema anterior.Los constructores implícitos efectivamente hacen lo mismo, pero están controlados por el tipo de conversión:
Implicaciones de sobrecargas, conversiones y coerción proporcionadas por el compilador
Considerar:
Si queremos que la cantidad
x
sea tratada como un número real durante la división (es decir, ser 6.5 en lugar de redondearse a 6), solo necesitamos cambiar atypedef double Amount
.Eso es bueno, pero no habría sido demasiado trabajo hacer que el código explícitamente "escriba correcto":
Pero considere que podemos transformar la primera versión en
template
:Debido a esas pequeñas "características de conveniencia", se puede crear una instancia tan fácil para cualquiera
int
odouble
para trabajar según lo previsto. Sin estas características, necesitaríamos conversiones explícitas, rasgos de tipo y / o clases de políticas, algún desorden detallado y propenso a errores como:Por lo tanto, la sobrecarga del operador proporcionada por el compilador para tipos incorporados, conversiones estándar, fundición / coerción / constructores implícitos: todos contribuyen con un sutil soporte para el polimorfismo. Desde la definición en la parte superior de esta respuesta, abordan "encontrar y ejecutar código apropiado para el tipo" mediante el mapeo:
"lejos" de los tipos de parámetros
de los muchos tipos de datos manejadores de códigos algorítmicos polimórficos
al código escrito para un número (potencialmente menor) de (el mismo u otro) tipo.
"para" tipos paramétricos de valores de tipo constante
Ellos no establecen contextos polimórficos por sí mismos, pero sí ayudar a potenciar / código de simplificar el interior de dichos contextos.
Puede sentirse engañado ... no parece mucho. La importancia es que en contextos polimórficos paramétricos (es decir, dentro de plantillas o macros), estamos tratando de admitir una amplia gama de tipos arbitrariamente, pero a menudo queremos expresar operaciones sobre ellos en términos de otras funciones, literales y operaciones que fueron diseñadas para un Pequeño conjunto de tipos. Reduce la necesidad de crear funciones o datos casi idénticos por tipo cuando la operación / valor es lógicamente el mismo. Estas características cooperan para agregar una actitud de "mejor esfuerzo", haciendo lo que intuitivamente se espera mediante el uso de las funciones y los datos disponibles limitados y solo se detienen con un error cuando existe una ambigüedad real.
Esto ayuda a limitar la necesidad de código polimórfico que admita código polimórfico, dibujando una red más ajustada alrededor del uso del polimorfismo para que el uso localizado no fuerce el uso generalizado, y haciendo que los beneficios del polimorfismo estén disponibles según sea necesario sin imponer los costos de tener que exponer la implementación en tiempo de compilación, tener múltiples copias de la misma función lógica en el código objeto para admitir los tipos utilizados, y al hacer despacho virtual en lugar de llamadas en línea o al menos llamadas resueltas en tiempo de compilación. Como es típico en C ++, el programador tiene mucha libertad para controlar los límites dentro de los cuales se usa el polimorfismo.
fuente
dynamic_cast
requiere un "puntero o un valor de tipo polimórfico". Saludos y hth.,En C ++, la distinción importante es el enlace en tiempo de ejecución frente al tiempo de compilación. Ad-hoc vs. paramétrico realmente no ayuda, como explicaré más adelante.
Nota: el polimorfismo en tiempo de ejecución aún puede resolverse en tiempo de compilación, pero eso es solo optimización. La necesidad de admitir la resolución del tiempo de ejecución de manera eficiente y la compensación contra otros problemas, es parte de lo que llevó a que las funciones virtuales sean lo que son. Y eso es realmente clave para todas las formas de polimorfismo en C ++: cada una surge de diferentes conjuntos de compensaciones realizadas en un contexto diferente.
La sobrecarga de funciones y la sobrecarga del operador son lo mismo en todos los aspectos importantes. Los nombres y la sintaxis para usarlos no afectan el polimorfismo.
Las plantillas le permiten especificar muchas sobrecargas de funciones a la vez.
Hay otro conjunto de nombres para la misma idea de resolución-tiempo ...
Estos nombres están más asociados con OOP, por lo que es un poco extraño decir que una plantilla u otra función que no es miembro usa el enlace temprano.
Para comprender mejor la relación entre las funciones virtuales y la sobrecarga de funciones, también es útil comprender la diferencia entre "despacho único" y "despacho múltiple". La idea puede entenderse como una progresión ...
Obviamente, hay más en OOP que una excusa para nominar un parámetro como especial, pero esa es una parte de él. Y en relación con lo que dije sobre las compensaciones: el despacho único es bastante fácil de hacer de manera eficiente (la implementación habitual se llama "tablas virtuales"). El despacho múltiple es más incómodo, no solo en términos de eficiencia, sino también para la compilación por separado. Si tiene curiosidad, puede buscar "el problema de expresión".
Así como es un poco extraño usar el término "enlace temprano" para funciones que no son miembros, es un poco extraño usar los términos "despacho único" y "despacho múltiple" donde el polimorfismo se resuelve en tiempo de compilación. Por lo general, se considera que C ++ no tiene un despacho múltiple, lo que se considera un tipo particular de resolución de tiempo de ejecución. Sin embargo, la sobrecarga de funciones puede verse como un despacho múltiple realizado en tiempo de compilación.
Volviendo al polimorfismo paramétrico versus ad-hoc, estos términos son más populares en la programación funcional, y no funcionan en C ++. Aún así...
El polimorfismo paramétrico significa que tiene tipos como parámetros, y se usa exactamente el mismo código independientemente del tipo que utilice para esos parámetros.
El polimorfismo ad-hoc es ad-hoc en el sentido de que proporciona un código diferente según los tipos particulares.
La sobrecarga y las funciones virtuales son ejemplos de polimorfismo ad-hoc.
De nuevo, hay algunos sinónimos ...
Excepto que estos no son sinónimos, aunque comúnmente se tratan como si lo fueran, y es ahí donde es probable que surja confusión en C ++.
El razonamiento detrás de tratar estos como sinónimos es que al restringir el polimorfismo a clases particulares de tipos, es posible utilizar operaciones específicas para esas clases de tipos. La palabra "clases" aquí se puede interpretar en el sentido OOP, pero en realidad solo se refiere a conjuntos (generalmente denominados) de tipos que comparten ciertas operaciones.
Por lo tanto, el polimorfismo paramétrico generalmente se toma (al menos por defecto) para implicar polimorfismo sin restricciones. Debido a que se usa el mismo código independientemente de los parámetros de tipo, las únicas operaciones compatibles son aquellas que funcionan para todos los tipos. Al dejar el conjunto de tipos sin restricciones, limita severamente el conjunto de operaciones que puede aplicar a esos tipos.
En, por ejemplo, Haskell, puedes tener ...
El
a
aquí es un tipo polimórfico sin restricciones. Podría ser cualquier cosa, por lo que no hay mucho que podamos hacer con valores de ese tipo.Aquí,
a
está obligado a ser miembro de laNum
clase, tipos que actúan como números. Esa restricción le permite hacer cosas numéricas con esos valores, como agregarlos. Incluso la3
inferencia de tipo es polimórfica, se da cuenta de que te refieres al3
tipoa
.Pienso en esto como polimorfismo paramétrico restringido. Solo hay una implementación, pero solo se puede aplicar en casos restringidos. El aspecto ad-hoc es la elección de cuál
+
y3
de usar. Cada "instancia" deNum
tiene su propia implementación distinta de estos. Así que incluso en Haskell "paramétrico" y "sin restricciones" no son realmente sinónimos, ¡no me culpen, no es mi culpa!En C ++, tanto la sobrecarga como las funciones virtuales son polimorfismos ad-hoc. La definición de polimorfismo ad-hoc no importa si la implementación se selecciona en tiempo de ejecución o en tiempo de compilación.
C ++ se acerca mucho al polimorfismo paramétrico con plantillas si cada parámetro de la plantilla tiene tipo
typename
. Hay parámetros de tipo, y hay una única implementación sin importar qué tipos se usen. Sin embargo, la regla "La falla de sustitución no es un error" significa que surgen restricciones implícitas como resultado del uso de operaciones dentro de la plantilla. Las complicaciones adicionales incluyen la especialización de plantillas para proporcionar plantillas alternativas, implementaciones diferentes (ad-hoc).Entonces, en cierto modo, C ++ tiene polimorfismo paramétrico, pero está restringido implícitamente y podría ser anulado por alternativas ad-hoc, es decir, esta clasificación realmente no funciona para C ++.
fuente
a
aquí hay un tipo polimórfico [...] sin restricciones, así que no hay mucho que podamos hacer con valores de ese tipo". era de interés: en C ++ sin Conceptos, no está restringido a intentar solo un conjunto específico de operaciones en un argumento de un tipo especificado como parámetro de plantilla ... las bibliotecas como los conceptos de impulso funcionan de otra manera, asegurándose de que el tipo sea compatible con las operaciones usted especifica, en lugar de evitar el uso accidental de operaciones adicionales.En cuanto al polimorfismo ad-hoc, significa sobrecarga de funciones o sobrecarga del operador. Mira aquí:
http://en.wikipedia.org/wiki/Ad-hoc_polymorphism
En cuanto al polimorfismo paramétrico, las funciones de plantilla también se pueden contar porque no necesariamente incluyen parámetros de tipos FIJOS. Por ejemplo, una función puede ordenar una matriz de enteros y también puede ordenar una matriz de cadenas, etc.
http://en.wikipedia.org/wiki/Parametric_polymorphism
fuente
<
operadores similares y similares). En Haskell, expresarías ese requisito explícitamente usando la claseOrd
. El hecho de que obtenga un diferente<
según el tipo particular (según lo suministrado por la instancia deOrd
) se consideraría polimorfismo ad-hoc.Esto puede no ser de ninguna ayuda, pero hice esto para presentarles a mis amigos la programación al darles funciones definidas, como
START
, yEND
para la función principal, por lo que no fue demasiado desalentador (solo usaron el archivo main.cpp ). Contiene clases y estructuras polimórficas, plantillas, vectores, matrices, directivas de preprocesador, amistad, operadores y punteros (todo lo cual probablemente debería saber antes de intentar el polimorfismo):Nota: no está terminado, pero puedes hacerte una idea
main.cpp
main.h
fuente
Aquí hay un ejemplo básico usando clases polimórficas
fuente
Polimorfismo significa muchas formas como tal, se utiliza para que un operador actúe de manera diferente en diferentes instancias. El polimorfismo se usa para implementar la herencia. Por ejemplo, hemos definido un dibujo fn () para una forma de clase, luego el dibujo fn se puede implementar para dibujar círculos, cuadros, triángulos y otras formas. (que son objetos de la forma de la clase)
fuente
Si alguien dice CORTE a estas personas
¿Lo que sucederá?
Entonces, la representación anterior muestra qué es el polimorfismo (mismo nombre, comportamiento diferente) en OOP.
Si vas a una entrevista y el entrevistador te pide que digas / muestres un ejemplo en vivo de polimorfismo en la misma habitación en la que estamos sentados, di-
Respuesta: puerta / ventanas
¿Se pregunta cómo?
A través de la puerta / ventana: puede venir una persona, puede llegar el aire, puede venir la luz, puede venir la lluvia, etc.
es decir, una forma de comportamiento diferente (polimorfismo).
Para entenderlo mejor y de manera simple, utilicé el ejemplo anterior. Si necesita una referencia para el código, siga las respuestas anteriores.
fuente