Polimorfismo en c ++

HASTA DONDE SE:

C ++ proporciona tres tipos diferentes de polymorphism.

  • Funciones virtuales
  • Sobrecarga de nombre de función
  • Sobrecarga del operador

Además de los tres tipos anteriores de polymorphism, existen otros tipos de polymorphism:

  • tiempo de ejecución
  • tiempo de comstackción
  • polymorphism ad-hoc
  • polymorphism paramétrico

Sé que el polymorphism en tiempo de ejecución se puede lograr mediante funciones virtuales y el polymorphism estático se puede lograr mediante funciones de plantilla

Pero para los otros dos

  • polymorphism ad-hoc
  • polymorphism paramétrico dice el sitio web ,

polymorphism ad-hoc:

Si el rango de tipos reales que pueden usarse es finito y las combinaciones deben especificarse individualmente antes de su uso, esto se denomina polymorphism ad-hoc.

polymorphism 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 cantidad de tipos nuevos, se llama polymorphism 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 muchas nuevas salidas de sus universidades.

Comprensión de / requisitos para el polymorphism

Para comprender el polymorphism, como se usa el término en Ciencias de la computación, es útil partir de una simple prueba y definición de este. Considerar:

Type1 x; Type2 y; f(x); f(y); 

Aquí, f() es para realizar alguna operación y se le están dando valores x e y como entradas.

Para exhibir polymorphism, f() debe ser capaz de operar con valores de al menos dos tipos distintos (por ejemplo, int y double ), encontrar y ejecutar un código distintivo apropiado para el tipo.


Mecanismos de C ++ para el polymorphism

Polimorfismo explícito especificado por el progtwigdor

Puede escribir f() manera que pueda operar en varios tipos de cualquiera de las siguientes maneras:

  • Preprocesamiento:

     #define f(X) ((X) += 2) // (note: in real code, use a longer uppercase name for a macro!) 
  • Sobrecarga:

     void f(int& x) { x += 2; } void f(double& x) { x += 2; } 
  • Plantillas:

     template  void f(T& x) { x += 2; } 
  • Despacho virtual:

     struct Base { virtual Base& operator+=(int) = 0; }; struct X : Base { X(int n) : n_(n) { } X& operator+=(int n) { n_ += n; return *this; } int n_; }; struct Y : Base { Y(double n) : n_(n) { } Y& operator+=(int n) { n_ += n; return *this; } double n_; }; void f(Base& x) { x += 2; } // run-time polymorphic dispatch 

Otros mecanismos relacionados

El polymorphism proporcionado por el comstackdor para los tipos incorporados, las conversiones estándar y el colado / coerción se analizan más adelante para que esté completo, ya que:

  • por lo general, se los entiende intuitivamente de todos modos (lo que garantiza una reacción ” oh, eso “),
  • impactan en el umbral al requerir, y la fluidez en el uso, los mecanismos anteriores, y
  • la explicación es una distracción complicada de conceptos más importantes.

Terminología

Categorización adicional

Dados los mecanismos polimórficos anteriores, podemos categorizarlos de varias maneras:

  • ¿Cuándo se selecciona el código específico del tipo polimórfico?

    • El tiempo de ejecución significa que el comstackdor debe generar código para todos los tipos que el progtwig pueda manejar mientras se ejecuta, y en el tiempo de ejecución se selecciona el código correcto ( despacho virtual )
    • El tiempo de comstackción significa que la elección del código específico del tipo se realiza durante la comstackción. Una consecuencia de esto: supongamos que un progtwig solo llama f arriba con argumentos int . Dependiendo del mecanismo polimórfico utilizado y las elecciones de delimitación, el comstackdor puede evitar generar cualquier código para f(double) o el código generado puede descartarse en algún momento de la comstackción. o vincular. ( todos los mecanismos anteriores, excepto el despacho virtual )
  • ¿Qué tipos son compatibles?

    • Ad-hoc significa que proporciona un código explícito para soportar cada tipo (por ejemplo, sobrecarga, especialización de plantilla); explícitamente agregas soporte “para esto” (según el significado ad hoc ) tipo, algún otro “esto”, y tal vez “eso” también ;-).
    • Esto significa que puede intentar utilizar la función para varios tipos de parámetros sin hacer nada específicamente para habilitar su compatibilidad (p. Ej., 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 siendo irrelevante. Los “conceptos” recortados de C ++ 11 ayudan a express y hacer cumplir esas expectativas, esperemos que lo conviertan en un Estándar posterior.

      • El polymorphism paramétrico proporciona tipeo de pato , un concepto atribuido a James Whitcomb Riley que aparentemente dijo “Cuando veo un pájaro que camina como un pato y nada como un pato y grazna como un pato, lo llamo pato”. .

         template  void do_ducky_stuff(const Duck& x) { x.walk().swim().quack(); } do_ducky_stuff(Vilified_Cygnet()); 
    • El polymorphism de subtipo (también conocido como inclusión) le permite trabajar en tipos nuevos 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 polymorphism paramétrico. Por ejemplo, podría codificar que cuando el tipo de datos que está procesando tenga un miembro .size() usará una función, de lo contrario, otra función que no necesita .size() (pero presumiblemente sufre de alguna manera, por ejemplo, usar el strlen() lento strlen() o no imprimir como útil un mensaje en el registro). También puede especificar comportamientos ad-hoc cuando se crea una instancia de la plantilla con parámetros específicos, ya sea dejando algunos parámetros paramétricos ( especialización de plantilla parcial ) o no ( especialización completa ).

“Polimórfico”

Alf Steinbach comenta que en el Estándar polimórfico de C ++ solo se refiere al polymorphism en tiempo de ejecución que utiliza despacho virtual. Comp. General Sci. el significado es más inclusivo, según el glosario del creador de C ++ Bjarne Stroustrup ( http://www.stroustrup.com/glossary.html ):

polymorphism: proporciona una interfaz única para entidades de diferentes tipos. Las funciones virtuales proporcionan un polymorphism dynamic (en tiempo de ejecución) a través de una interfaz proporcionada por una clase base. Las funciones y plantillas sobrecargadas proporcionan polymorphism estático (en tiempo de comstackción). TC ++ PL 12.2.6, 13.6.1, D y E 2.9.

Esta respuesta, como la pregunta, relaciona las características de C ++ con las Comp. Sci. terminología.

Discusión

Con el Estándar C ++ usando una definición más restringida de “polymorphism” que el Comp. Sci. comunidad, para asegurar que la comprensión mutua para su audiencia considere …

  • usando una terminología inequívoca (“¿podemos hacer que este código sea reutilizable para otros tipos?” o “¿podemos usar despacho virtual?” en lugar de “¿podemos hacer que este código sea polimórfico?”), y / o
  • definiendo claramente su terminología.

Aún así, lo que es crucial para ser un gran progtwigdor de C ++ es entender qué polymorphism realmente está haciendo por ti …

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 concuerdan con sus necesidades reales.

Trajes de polymorphism en tiempo de ejecución:

  • entrada procesada por métodos de fábrica y escupida como una colección heterogénea de objetos manejada a través de Base* s,
  • implementación elegida en tiempo de ejecución basada en archivos de configuración, interruptores de línea de comandos, configuración de UI, etc.
  • la implementación varió en el tiempo de ejecución, como para un patrón de máquina de estado.

Cuando no hay un controlador claro para el polymorphism en tiempo de ejecución, a menudo son preferibles las opciones de tiempo de comstackción. Considerar:

  • el aspecto de comstackr-lo que se llama de las clases con plantillas es preferible a las interfaces gordas que fallan en el tiempo de ejecución
  • SFINAE
  • CRTP
  • optimizaciones (muchas incluyendo alineación y eliminación de código muerto, desenrollado de bucles, matrices estáticas basadas en stack frente a montón)
  • __FILE__ , __LINE__ , cadena de concatenación literal y otras capacidades únicas de macros (que siguen siendo malvadas ;-))
  • el uso de semántica de prueba de macros y plantillas es compatible, pero no restringe artificialmente la forma en que se brinda dicha compatibilidad (ya que el despacho virtual tiende a requerir reemplazos de función de miembro que coincidan exactamente)

Otros mecanismos que apoyan el polymorphism

Como se prometió, para completar, se tratan varios temas periféricos:

  • sobrecargas proporcionadas por el comstackdor
  • conversiones
  • moldes / coacción

Esta respuesta concluye con una discusión sobre cómo lo anterior se combina para potenciar y simplificar el código polimórfico, especialmente el polymorphism paramétrico (plantillas y macros).

Mecanismos de mapeo para operaciones específicas de tipo

> Sobrecargas implícitas proporcionadas por el comstackdor

Conceptualmente, el comstackdor sobrecarga muchos operadores para tipos incorporados. No es conceptualmente diferente de la sobrecarga especificada por el usuario, pero aparece en la lista ya que se pasa por alto fácilmente. Por ejemplo, puede agregar a int s y double s usando la misma notación x += 2 y el comstackdor produce:

  • instrucciones de CPU específicas del tipo
  • un resultado del mismo tipo.

La sobrecarga se extiende a los tipos definidos por el usuario:

 std::string x; int y = 0; x += 'c'; y += 'c'; 

Las sobrecargas proporcionadas por el comstackdor para los tipos básicos son comunes en los lenguajes informáticos de alto nivel (3GL +), y la discusión explícita del polymorphism generalmente implica algo más. (2GLs – lenguajes ensamblados – a menudo requieren que el progtwigdor use explícitamente diferentes mnemónicos para diferentes tipos).

> Conversiones estándar

La cuarta sección del Estándar de C ++ describe las conversiones estándar.

El primer punto se resume muy bien (a partir de un borrador anterior, con suerte, sustancialmente correcto):

-1- Las conversiones estándar son conversiones implícitas definidas para tipos incorporados. Clause conv enumera el conjunto completo de dichas conversiones. Una secuencia de conversión estándar es una secuencia de conversiones estándar en el siguiente orden:

  • Cero o una conversión del siguiente conjunto: conversión de lvalue a 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 coma flotante, conversiones de integral flotante, conversiones de puntero, conversiones de puntero a miembro y conversiones booleanas.

  • Cero o una conversión de calificación.

[Nota: una secuencia de conversión estándar puede estar vacía, es decir, puede consistir en ninguna conversión. ] Se aplicará una secuencia de conversión estándar a una expresión si es necesario para convertirla a un tipo de destino requerido.

Estas conversiones permiten códigos tales como:

 double a(double x) { return x + 2; } a(3.14); a(42); 

Aplicando la prueba anterior:

Para ser polimórfico, [ a() ] debe ser capaz de operar con valores de al menos dos tipos distintos (por ejemplo, int y double ), encontrar y ejecutar un código apropiado para el tipo .

a() sí mismo ejecuta el código específicamente para el double y, por lo tanto, no es polimórfico.

Pero, en la segunda llamada a a() el comstackdor sabe generar el código apropiado para el tipo para una “promoción de punto flotante” (Estándar §4) para convertir 42 a 42.0 . Ese código adicional está en la función de llamada . Discutiremos el significado 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:

 int a, b; if (std::cin >> a >> b) f(a, b); 

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 otras de las conversiones estándar en el tema anterior.

Los constructores implícitos efectivamente hacen lo mismo, pero están controlados por el tipo cast-to:

 f(const std::string& x); f("hello"); // invokes `std::string::string(const char*)` 

Implicaciones de sobrecargas, conversiones y coacciones proporcionadas por el comstackdor

Considerar:

 void f() { typedef int Amount; Amount x = 13; x /= 2; std::cout << x * 1.1; } 

Si queremos que la cantidad x se trate como un número real durante la división (es decir, sea 6.5 en lugar de redondearse a 6), solo necesitamos cambiar a typedef double Amount .

Eso está bien, pero no habría sido demasiado trabajo hacer que el código explícitamente "escriba correcto":

 void f() void f() { { typedef int Amount; typedef double Amount; Amount x = 13; Amount x = 13.0; x /= 2; x /= 2.0; std::cout << double(x) * 1.1; std::cout << x * 1.1; } } 

Pero, considere que podemos transformar la primera versión en una template :

 template  void f() { Amount x = 13; x /= 2; std::cout << x * 1.1; } 

Es debido a esas pequeñas "funciones de conveniencia" que puede ser fácilmente instanciado para int o double y funciona como se esperaba. Sin estas características, necesitaríamos conversiones explícitas, rasgos de tipo y / o clases de políticas, algunos desordenados y propensos a errores como:

 template  void f() { Amount x = Policy::thirteen; x /= static_cast(2); std::cout << traits::to_double(x) * 1.1; } 

Por lo tanto, la sobrecarga del operador proporcionada por el comstackdor para los tipos integrados, las conversiones estándar, el colado / la coerción / los constructores implícitos, todos contribuyen de forma sutil al polymorphism. De la definición en la parte superior de esta respuesta, abordan "encontrar y ejecutar el código apropiado para el tipo" mediante el mapeo:

  • "ausente" de los tipos de parámetros

    • de los muchos tipos de datos controladores de código algorítmico polimórficos

    • codificar escrito para un número (potencialmente menor) de (el mismo u otro) tipo.

  • "a" tipos paramétricos a partir de valores de tipo constante

No establecen contextos polimórficos por sí mismos, pero ayudan a potenciar / simplificar el código dentro 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 gama arbitrariamente grande de tipos, pero a menudo queremos express operaciones sobre ellos en términos de otras funciones, literales y operaciones que fueron diseñadas para una 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 se espera intuitivamente al usar las funciones y datos disponibles y limitados, y solo parando 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 estrecha alrededor del uso del polymorphism, por lo que el uso localizado no obliga al uso generalizado y hace que los beneficios del polymorphism estén disponibles según sea necesario sin imponer el costo de tener que exponer la implementación a tiempo de comstackción, tener múltiples copias de la misma función lógica en el código objeto para soportar los tipos usados, y al hacer despacho virtual en oposición a la inlining o al menos llamadas resueltas en tiempo de comstackción. Como es típico en C ++, el progtwigdor tiene mucha libertad para controlar los límites dentro de los cuales se usa el polymorphism.

En C ++, la distinción importante es el tiempo de ejecución frente al enlace en tiempo de comstackción. Ad-hoc vs. paramétrico realmente no ayuda, como explicaré más adelante.

 |----------------------+--------------| | Form | Resolved at | |----------------------+--------------| | function overloading | compile-time | | operator overloading | compile-time | | templates | compile-time | | virtual methods | run-time | |----------------------+--------------| 

Nota: el polymorphism de tiempo de ejecución aún puede resolverse en tiempo de comstackción, pero eso es solo optimización. La necesidad de respaldar la resolución en tiempo de ejecución de manera eficiente, y el intercambio frente a otros problemas, es parte de lo que llevó a las funciones virtuales a ser lo que son. Y eso es realmente clave para todas las formas de polymorphism en C ++: cada una surge de diferentes conjuntos de concesiones hechas en un contexto diferente.

La sobrecarga de funciones y la sobrecarga del operador son lo mismo en todo lo que importa. Los nombres y la syntax para usarlos no afectan el polymorphism.

Las plantillas le permiten especificar muchas sobrecargas de funciones a la vez.

Hay otro conjunto de nombres para la misma idea de tiempo de resolución …

 |---------------+--------------| | early binding | compile-time | | late binding | run-time | |---------------+--------------| 

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 sea miembro utiliza el enlace anticipado.

Para comprender mejor la relación entre las funciones virtuales y la sobrecarga de funciones, también es útil comprender la diferencia entre “envío único” y “envío múltiple”. La idea puede ser entendida como una progresión …

  • Primero, hay funciones monomórficas. La implementación de la función se identifica de manera única por el nombre de la función. Ninguno de los parámetros es especial.
  • Entonces, hay un solo despacho. Uno de los parámetros se considera especial y se usa (junto con el nombre) para identificar qué implementación usar. En OOP, tendemos a pensar en este parámetro como “el objeto”, listarlo antes del nombre de la función, etc.
  • Entonces, hay despacho múltiple. Cualquiera / todos los parámetros contribuyen a identificar qué implementación usar. Por lo tanto, una vez más, ninguno de los parámetros debe ser especial.

Obviamente, hay más en OOP que una excusa para designar un parámetro como especial, pero esa es una parte de ello. Y relacionándome con lo que dije acerca de las compensaciones: el envío único es bastante fácil de hacer de manera eficiente (la implementación habitual se llama “tablas virtuales”). El envío múltiple es más incómodo, no solo en términos de eficiencia, sino también para una comstackción separada. Si tiene curiosidad, puede buscar “el problema de la expresión”.

Así como es un poco extraño usar el término “vinculación anticipada” para las funciones que no son miembros, es un poco extraño usar los términos “envío único” y “despacho múltiple” donde el polymorphism se resuelve en tiempo de comstackción. Por lo general, se considera que C ++ no tiene despacho múltiple, que se considera un tipo particular de resolución en tiempo de ejecución. Sin embargo, la sobrecarga de funciones puede verse como un despacho múltiple hecho en tiempo de comstackción.

Volviendo al polymorphism paramétrico frente al ad hoc, estos términos son más populares en la progtwigción funcional, y no funcionan del todo en C ++. Aún así…

El polymorphism paramétrico significa que tiene tipos como parámetros, y el mismo código se usa independientemente del tipo que use para esos parámetros.

El polymorphism ad-hoc es ad-hoc en el sentido de que usted proporciona un código diferente dependiendo de los tipos particulares.

La sobrecarga y las funciones virtuales son ejemplos de polymorphism ad-hoc.

De nuevo, hay algunos sinónimos …

 |------------+---------------| | parametric | unconstrained | | ad-hoc | constrained | |------------+---------------| 

Excepto que estos no son sinónimos, aunque comúnmente se tratan como si lo fueran, y es allí donde es probable que surja confusión en C ++.

El razonamiento detrás de tratar estos como sinónimos es que al restringir el polymorphism a clases particulares de tipos, es posible usar operaciones específicas para esas clases de tipos. La palabra “clases” aquí se puede interpretar en el sentido de OOP, pero en realidad solo se refiere a conjuntos (generalmente nombrados) de tipos que comparten ciertas operaciones.

Por lo tanto, el polymorphism paramétrico generalmente se toma (al menos por defecto) para implicar un polymorphism no restringido. Debido a que se usa el mismo código independientemente de los parámetros de tipo, las únicas operaciones soportables 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 eg Haskell, puedes tener …

 myfunc1 :: Bool -> a -> a -> a myfunc1 cxy = if c then x else y 

El aquí es un tipo polimórfico no restringido. Podría ser cualquier cosa, así que no hay mucho que podamos hacer con valores de ese tipo.

 myfunc2 :: Num a => a -> a myfunc2 x = x + 3 

Aquí, a está obligado a ser miembro de la clase Num , tipos que actúan como números. Esa restricción le permite hacer cosas numéricas con esos valores, como agregarlos. Incluso el 3 es una inferencia de tipo polimórfico, se da cuenta de que te refieres al 3 de tipo a .

Pienso en esto como un polymorphism paramétrico restringido. Solo hay una implementación, pero solo se puede aplicar en casos limitados. El aspecto ad-hoc es la elección de qué + y 3 usar. Cada “instancia” de Num tiene su propia implementación distinta de estos. Así que incluso en Haskell “paramétrico” y “no restringido” no son sinónimos, ¡no me culpes, no es mi culpa!

En C ++, tanto la sobrecarga como las funciones virtuales son polymorphisms ad-hoc. La definición de polymorphism ad-hoc no importa si la implementación se selecciona en tiempo de ejecución o en tiempo de comstackción.

C ++ se acerca mucho al polymorphism paramétrico con plantillas si cada parámetro de plantilla tiene el tipo typename . Hay parámetros de tipo, y hay una sola implementación sin importar qué tipos se usan. 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 plantilla para proporcionar plantillas alternativas: implementaciones diferentes (ad-hoc).

De modo que C ++ tiene polymorphism paramétrico, pero está implícitamente restringido y podría ser anulado por alternativas ad-hoc, es decir, esta clasificación realmente no funciona para C ++.

En cuanto al polymorphism ad-hoc, significa sobrecarga de funciones o sobrecarga del operador. Mira aquí:

http://en.wikipedia.org/wiki/Ad-hoc_polymorphism

En cuanto al polymorphism paramétrico, también se pueden contar las funciones de plantilla porque no necesariamente toman en cuenta los parámetros de los 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

Esto puede no ser de ayuda, pero hice esto para presentarles a mis amigos la progtwigción dando funciones definidas, como START , y END para la función principal, así 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 (todos los cuales probablemente deberías saber antes de intentar el polymorphism):

Nota: no está terminado, pero puede hacerse una idea

main.cpp

 #include "main.h" #define ON_ERROR_CLEAR_SCREEN false START Library MyLibrary; Book MyBook("My Book", "Me"); MyBook.Summarize(); MyBook += "Hello World"; MyBook += "HI"; MyBook.EditAuthor("Joe"); MyBook.EditName("Hello Book"); MyBook.Summarize(); FixedBookCollection FBooks("Fairytale Books"); FairyTale MyTale("Tale", "Joe"); FBooks += MyTale; BookCollection E("E"); MyLibrary += E; MyLibrary += FBooks; MyLibrary.Summarize(); MyLibrary -= FBooks; MyLibrary.Summarize(); FixedSizeBookCollection<5> Collection("My Fixed Size Collection"); /* Extension Work */ Book* Duplicate = MyLibrary.DuplicateBook(&MyBook); /* Extension Work */ Duplicate->Summarize(); END 

main.h

 #include  #include  #include  #include  #include  #include  #ifndef __cplusplus #error Not C++ #endif #define START int main(void)try{ #define END GET_ENTER_EXIT return(0);}catch(const std::exception& e){if(ON_ERROR_CLEAR_SCREEN){system("cls");}std::cerr << "Error: " << e.what() << std::endl; GET_ENTER_EXIT return (1);} #define GET_ENTER_EXIT std::cout << "Press enter to exit" << std::endl; getchar(); class Book; class Library; typedef std::vector Books; bool sContains(const std::string s, const char c){ return (s.find(c) != std::string::npos); } bool approve(std::string s){ return (!sContains(s, '#') && !sContains(s, '%') && !sContains(s, '~')); } template  bool isBook(){ return (typeid(C) == typeid(Book) || std::is_base_of()); } template class DuplicatableClass{ public: ClassToDuplicate* Duplicate(ClassToDuplicate ToDuplicate){ return new ClassToDuplicate(ToDuplicate); } }; class Book : private DuplicatableClass{ friend class Library; friend struct BookCollection; public: Book(const char* Name, const char* Author) : name_(Name), author_(Author){} void operator+=(const char* Page){ pages_.push_back(Page); } void EditAuthor(const char* AuthorName){ if(approve(AuthorName)){ author_ = AuthorName; } else{ std::ostringstream errorMessage; errorMessage << "The author of the book " << name_ << " could not be changed as it was not approved"; throw std::exception(errorMessage.str().c_str()); } } void EditName(const char* Name){ if(approve(Name)){ name_ = Name; } else{ std::ostringstream errorMessage; errorMessage << "The name of the book " << name_ << " could not be changed as it was not approved"; throw std::exception(errorMessage.str().c_str()); } } virtual void Summarize(){ std::cout << "Book called " << name_ << "; written by " << author_ << ". Contains " << pages_.size() << ((pages_.size() == 1) ? " page:" : ((pages_.size() > 0) ? " pages:" : " pages")) << std::endl; if(pages_.size() > 0){ ListPages(std::cout); } } private: std::vector pages_; const char* name_; const char* author_; void ListPages(std::ostream& output){ for(int i = 0; i < pages_.size(); ++i){ output << pages_[i] << std::endl; } } }; class FairyTale : public Book{ public: FairyTale(const char* Name, const char* Author) : Book(Name, Author){} }; struct BookCollection{ friend class Library; BookCollection(const char* Name) : name_(Name){} virtual void operator+=(const Book& Book)try{ Collection.push_back(&Book); }catch(const std::exception& e){ std::ostringstream errorMessage; errorMessage << e.what() << " - on line (approx.) " << (__LINE__ -3); throw std::exception(errorMessage.str().c_str()); } virtual void operator-=(const Book& Book){ for(int i = 0; i < Collection.size(); ++i){ if(Collection[i] == &Book){ Collection.erase(Collection.begin() + i); return; } } std::ostringstream errorMessage; errorMessage << "The Book " << Book.name_ << " was not found, and therefore cannot be erased"; throw std::exception(errorMessage.str().c_str()); } private: const char* name_; Books Collection; }; template struct FixedBookCollection : public BookCollection{ FixedBookCollection(const char* Name) : BookCollection(Name){ if(!isBook()){ std::ostringstream errorMessage; errorMessage << "The type " << typeid(FixedType).name() << " cannot be initialized as a FixedBookCollection"; throw std::exception(errorMessage.str().c_str()); delete this; } } void operator+=(const FixedType& Book)try{ Collection.push_back(&Book); }catch(const std::exception& e){ std::ostringstream errorMessage; errorMessage << e.what() << " - on line (approx.) " << (__LINE__ -3); throw std::exception(errorMessage.str().c_str()); } void operator-=(const FixedType& Book){ for(int i = 0; i < Collection.size(); ++i){ if(Collection[i] == &Book){ Collection.erase(Collection.begin() + i); return; } } std::ostringstream errorMessage; errorMessage << "The Book " << Book.name_ << " was not found, and therefore cannot be erased"; throw std::exception(errorMessage.str().c_str()); } private: std::vector Collection; }; template struct FixedSizeBookCollection : private std::array{ FixedSizeBookCollection(const char* Name) : name_(Name){ if(Size < 1){ throw std::exception("A fixed size book collection cannot be smaller than 1"); currentPos = 0; } } void operator+=(const Book& Book)try{ if(currentPos + 1 > Size){ std::ostringstream errorMessage; errorMessage << "The FixedSizeBookCollection " << name_ << "'s size capacity has been overfilled"; throw std::exception(errorMessage.str().c_str()); } this->at(currentPos++) = &Book; }catch(const std::exception& e){ std::ostringstream errorMessage; errorMessage << e.what() << " - on line (approx.) " << (__LINE__ -3); throw std::exception(errorMessage.str().c_str()); } private: const char* name_; int currentPos; }; class Library : private std::vector{ public: void operator+=(const BookCollection& Collection){ for(int i = 0; i < size(); ++i){ if((*this)[i] == &Collection){ std::ostringstream errorMessage; errorMessage << "The BookCollection " << Collection.name_ << " was already in the library, and therefore cannot be added"; throw std::exception(errorMessage.str().c_str()); } } push_back(&Collection); } void operator-=(const BookCollection& Collection){ for(int i = 0; i < size(); ++i){ if((*this)[i] == &Collection){ erase(begin() + i); return; } } std::ostringstream errorMessage; errorMessage << "The BookCollection " << Collection.name_ << " was not found, and therefore cannot be erased"; throw std::exception(errorMessage.str().c_str()); } Book* DuplicateBook(Book* Book)const{ return (Book->Duplicate(*Book)); } void Summarize(){ std::cout << "Library, containing " << size() << ((size() == 1) ? " book collection:" : ((size() > 0) ? " book collections:" : " book collections")) << std::endl; if(size() > 0){ for(int i = 0; i < size(); ++i){ std::cout << (*this)[i]->name_ << std::endl; } } } }; 

Aquí hay un ejemplo básico usando clases polimórficas

 #include  class Animal{ public: Animal(const char* Name) : name_(Name){/* Add any method you would like to perform here*/ virtual void Speak(){ std::cout << "I am an animal called " << name_ << std::endl; } const char* name_; }; class Dog : public Animal{ public: Dog(const char* Name) : Animal(Name) {/*...*/} void Speak(){ std::cout << "I am a dog called " << name_ << std::endl; } }; int main(void){ Animal Bob("Bob"); Dog Steve("Steve"); Bob.Speak(); Steve.Speak(); //return (0); } 

Polimorfismo significa muchas formas como tal, se usa para que un operador actúe de manera diferente bajo diferentes instancias. El polymorphism se usa para implementar la herencia. Por ejemplo, hemos definido un dibujo fn () para una forma de clase y luego el dibujo fn se puede implementar para dibujar círculos, recuadros, triangularjs y otras formas. (que son objetos de la forma de clase)

Si alguien dice CORTAR a estas personas

 The Surgeon The Hair Stylist The Actor 

¿Lo que sucederá?

 The Surgeon would begin to make an incision. The Hair Stylist would begin to cut someone's hair. The Actor would abruptly stop acting out of the current scene, awaiting directorial guidance. 

Entonces, la representación de arriba muestra qué es el polymorphism (mismo nombre, comportamiento diferente) en OOP.

Si va a una entrevista y el entrevistador le pide que diga / muestre un ejemplo vivo de polymorphism en la misma habitación en la que estamos sentados, digamos-

Respuesta – Puerta / Windows

¿Preguntándose cómo?

Through Door / Window – a person can come, air can come, light can come, rain can come, etc.

ie One form different behavior(Polymorphism).

To understand it better and in a simple manner I used above example.. If you need reference for code follow above answers.

Intereting Posts