Polimorfismo em c ++

ATÉ ONDE SEI:

C ++ fornece três tipos diferentes de polymorphism.

  • Funções virtuais
  • Sobrecarga de nome de function
  • Sobrecarga do operador

Além dos três tipos de polymorphism acima, existem outros tipos de polymorphism:

  • tempo de execução
  • tempo de compilation
  • polymorphism ad-hoc
  • polymorphism paramétrico

Eu sei que o polymorphism de tempo de execução pode ser alcançado por funções virtuais e polymorphism estático pode ser alcançado por funções de modelo

Mas para os outros dois

  • polymorphism ad-hoc
  • polymorphism paramétrico o site diz :

polymorphism ad-hoc:

Se o intervalo de tipos reais que podem ser usados ​​é finito e as combinações devem ser especificadas individualmente antes do uso, isso é chamado de polymorphism ad-hoc.

polymorphism paramétrico:

Se todo o código é escrito sem menção de qualquer tipo específico e, portanto, pode ser usado de forma transparente com qualquer número de novos tipos, é chamado de polymorphism paramétrico.

eu mal posso entendê-los 🙁

Alguém pode explicar os dois se possível com um exemplo? Espero que as respostas a essas perguntas sejam úteis para muitos novos trabalhos das faculdades.

Entendimento de / requisitos para polymorphism

Para entender o polymorphism – como o termo é usado na Ciência da Computação – ele ajuda a partir de um simples teste e definição do mesmo. Considerar:

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

Aqui, f() é para executar alguma operação e está recebendo valores x e y como inputs.

Para exibir o polymorphism, f() deve ser capaz de operar com valores de pelo menos dois tipos distintos (por exemplo, int e double ), encontrando e executando código apropriado para o tipo distinto.


Mecanismos de C ++ para polymorphism

Polimorfismo especificado pelo programador explícito

Você pode escrever f() forma que possa operar em vários tipos de uma das seguintes maneiras:

  • Pré-processando:

     #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; } 
  • Modelos:

     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 

Outros mecanismos relacionados

O polymorphism fornecido pelo compilador para tipos incorporados, conversões padrão e conversão / coerção são discutidos posteriormente para fins de conclusão como:

  • eles são geralmente intuitivamente entendidos de qualquer maneira (garantindo uma reação ” oh, isso “),
  • eles afetam o limiar de exigência e uniformidade no uso dos mecanismos acima, e
  • A explicação é uma distração complicada dos conceitos mais importantes.

Terminologia

Categorização adicional

Dados os mecanismos polimórficos acima, podemos categorizá-los de várias maneiras:

  • Quando o código específico do tipo polimórfico é selecionado?

    • Tempo de execução significa que o compilador deve gerar código para todos os tipos que o programa pode manipular durante a execução e, em tempo de execução, o código correto é selecionado ( despacho virtual )
    • Tempo de compilation significa que a escolha do código específico do tipo é feita durante a compilation. Uma conseqüência disso: digamos, um programa chamado f acima com argumentos int – dependendo do mecanismo polimórfico usado e das opções inline, o compilador pode evitar gerar código para f(double) , ou o código gerado pode ser descartado em algum ponto da compilation ou vinculação. ( todos os mecanismos acima, exceto o despacho virtual )
  • Quais tipos são suportados?

    • Ad-hoc significa que você fornece código explícito para suportar cada tipo (por exemplo, sobrecarga, especialização de modelo); você explicitamente adiciona suporte “para isto” (como por ad hoc ‘s significado) tipo, algum outro “isto”, e talvez “isso” também ;-).
    • Significado paramétrico você pode apenas tentar usar a function para vários tipos de parâmetros sem fazer nada especificamente para ativar seu suporte para eles (por exemplo, modelos, macros). Um object com funções / operadores que agem como o modelo / macro espera 1 é tudo que o modelo / macro precisa fazer seu trabalho, com o tipo exato sendo irrelevante. Os “conceitos” cortados do C ++ 11 ajudam a expressar e impor tais expectativas – esperamos que eles se transformem em um padrão posterior.

      • Polimorfismo paramétrico fornece tipagem de pato – um conceito atribuído a James Whitcomb Riley que aparentemente disse “Quando eu vejo um pássaro que anda como um pato e nada como um pato e grasna como um pato, eu chamo esse pássaro de pato.” .

         template  void do_ducky_stuff(const Duck& x) { x.walk().swim().quack(); } do_ducky_stuff(Vilified_Cygnet()); 
    • O polymorphism de subtipo (aka inclusão) permite que você trabalhe em novos tipos sem atualizar o algoritmo / function, mas eles devem ser derivados da mesma class base (despacho virtual)

1 – Modelos são extremamente flexíveis. SFINAE (veja também std::enable_if ) efetivamente permite vários conjuntos de expectativas para o polymorphism paramétrico. Por exemplo, você pode codificar isso quando o tipo de dados que você está processando tiver um membro .size() você usará uma function, caso contrário, outra function que não precisa de .size() mas presumivelmente sofre de alguma forma – por exemplo, usando o strlen() mais lento ou não imprimindo uma mensagem útil no log). Você também pode especificar comportamentos ad-hoc quando o modelo é instanciado com parâmetros específicos, deixando alguns parâmetros paramétricos ( especialização de modelo parcial ) ou não ( especialização completa ).

“Polimórfico”

Alf Steinbach comenta que no polymorphism C ++ Standard se refere apenas ao polymorphism de tempo de execução usando o despacho virtual. Comp. Geral Sci. o significado é mais inclusivo, conforme o glossário do criador de C ++ Bjarne Stroustrup ( http://www.stroustrup.com/glossary.html ):

polymorphism – fornecendo uma única interface para entidades de diferentes tipos. As funções virtuais fornecem polymorphism dynamic (tempo de execução) por meio de uma interface fornecida por uma class base. Funções e modelos sobrecarregados fornecem polymorphism estático (tempo de compilation). TC ++ PL 12.2.6, 13.6.1, D & E 2.9.

Esta resposta – como a pergunta – relaciona resources do C ++ ao Comp. Sci. terminologia.

Discussão

Com o padrão C ++ usando uma definição mais restrita de “polymorphism” do que o Comp. Sci. comunidade, para garantir a compreensão mútua para o seu público considerar …

  • usando terminologia não ambígua (“podemos tornar este código reutilizável para outros tipos?” ou “podemos usar o despacho virtual?” em vez de “podemos tornar este código polimórfico?”), e / ou
  • definindo claramente sua terminologia.

Ainda assim, o que é crucial para ser um ótimo programador de C ++ é entender o que o polymorphism realmente está fazendo por você …

permitindo escrever código “algorítmico” uma vez e depois aplicá-lo a muitos tipos de dados

… e, em seguida, fique muito ciente de como os diferentes mecanismos polimórficos correspondem às suas necessidades reais.

Ternos de polymorphism em tempo de execução:

  • input processada por methods de fábrica e cuspida como uma coleção de objects heterogêneos manipulada via Base* s,
  • implementação escolhida em tempo de execução com base em arquivos de configuração, opções de linha de comando, configurações de interface do usuário, etc.
  • implementação variada em tempo de execução, como para um padrão de máquina de estado.

Quando não há um driver claro para o polymorphism de tempo de execução, as opções de tempo de compilation são geralmente preferíveis. Considerar:

  • o aspecto de compilation-o que é chamado de classs de modelos é preferível a interfaces de gordura que falham em tempo de execução
  • SFINAE
  • CRTP
  • otimizações (muitas incluindo inlining e eliminação de código morto, desenrolamento de loop, arrays baseados em pilha estática vs heap)
  • __FILE__ , __LINE__ , concatenação de string literal e outras capacidades únicas de macros (que permanecem más ;-))
  • o uso semântico de teste de modelos e macros é suportado, mas não restrinja artificialmente como esse suporte é fornecido (pois o despacho virtual tende a exigir a substituição de funções de membros exatamente correspondentes)

Outros mecanismos de apoio ao polymorphism

Como prometido, para completar, vários tópicos sobre periféricos são abordados:

  • sobrecargas fornecidas pelo compilador
  • conversões
  • moldes / coerção

Esta resposta conclui com uma discussão de como os itens acima se combinam para capacitar e simplificar o código polimórfico – especialmente o polymorphism paramétrico (modelos e macros).

Mecanismos para mapeamento para operações específicas de tipo

> Sobrecargas fornecidas pelo compilador implícito

Conceitualmente, o compilador sobrecarrega muitos operadores para tipos internos. Não é conceitualmente diferente da sobrecarga especificada pelo usuário, mas é listada como é facilmente ignorada. Por exemplo, você pode adicionar a int s e double s usando a mesma notação x += 2 e o compilador produz:

  • instruções de CPU específicas do tipo
  • um resultado do mesmo tipo.

Sobrecarga, em seguida, estende-se continuamente aos tipos definidos pelo usuário:

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

As sobrecargas fornecidas pelo compilador para tipos básicos são comuns em linguagens de computador de alto nível (3GL +), e a discussão explícita do polymorphism geralmente implica algo mais. (2GLs – assembly languages ​​- geralmente exigem que o programador use explicitamente mnemônicos diferentes para tipos diferentes).

> Conversões padrão

A quarta seção do C ++ Standard descreve as conversões padrão.

O primeiro ponto resume bem (de um rascunho antigo – esperançosamente ainda substancialmente correto):

-1- Conversões padrão são conversões implícitas definidas para tipos internos. A cláusula conv enumera o conjunto completo de tais conversões. Uma sequência de conversão padrão é uma sequência de conversões padrão na seguinte ordem:

  • Zero ou uma conversão do seguinte conjunto: conversão de lvalue para rvalue, conversão de array para ponteiro e conversão de function para ponteiro.

  • Zero ou uma conversão do conjunto a seguir: promoções integrais, promoção de ponto flutuante, conversões integrais, conversões de ponto flutuante, conversões integrais flutuantes, conversões de pointers, conversões de ponteiro para membro e conversões booleanas.

  • Zero ou uma conversão de qualificação.

[Nota: uma sequência de conversão padrão pode estar vazia, ou seja, não pode conter conversões. ] Uma sequência de conversão padrão será aplicada a uma expressão, se necessário, para convertê-la em um tipo de destino obrigatório.

Essas conversões permitem códigos como:

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

Aplicando o teste anterior:

Para ser polimórfico, [ a() ] deve ser capaz de operar com valores de pelo menos dois tipos distintos (por exemplo, int e double ), encontrando e executando o código apropriado para o tipo .

a() executa o código especificamente para double e, portanto, não é polimórfico.

Mas, na segunda chamada para a() o compilador sabe gerar código apropriado para o tipo para uma “promoção de ponto flutuante” (Standard §4) para converter 42 para 42.0 . Esse código extra está na function de chamada . Vamos discutir o significado disso na conclusão.

> Coerção, castings, construtores implícitos

Esses mecanismos permitem que classs definidas pelo usuário especifiquem comportamentos semelhantes às conversões padrão dos tipos incorporados. Vamos dar uma olhada:

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

Aqui, o object std::cin é avaliado em um contexto booleano, com a ajuda de um operador de conversão. Isso pode ser agrupado conceitualmente com “promoções integrais” e todas as conversões padrão no tópico acima.

Construtores implícitos efetivamente fazem a mesma coisa, mas são controlados pelo tipo cast-to:

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

Implicações de sobrecargas, conversões e coerções fornecidas pelo compilador

Considerar:

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

Se quisermos que a quantia x seja tratada como um número real durante a divisão (ou seja, 6.5 em vez de arredondada para 6), precisamos alterar para typedef double Amount .

Isso é bom, mas não teria sido muito trabalho para tornar o código explicitamente "tipo correto":

 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; } } 

Mas, considere que podemos transformar a primeira versão em um template :

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

É devido a esses pequenos "resources de conveniência" que ele pode ser facilmente instanciado para int ou double e funcionar conforme o esperado. Sem esses resources, precisaríamos de conversões explícitas, tipos de características e / ou classs de política, algumas mensagens detalhadas e propensas a erros como:

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

Portanto, a sobrecarga do operador fornecida pelo compilador para tipos internos, conversões Padrão, construtores de coerção / coerção / implícitos - todos eles contribuem com suporte sutil ao polymorphism. A partir da definição no topo desta resposta, eles abordam "encontrar e executar o código apropriado ao tipo" mapeando:

  • "ausente" dos tipos de parâmetro

    • dos muitos tipos de dados alças de código algorítmico polimórfico

    • codificar escrito para um número (potencialmente menor) de (o mesmo ou outro) tipos.

  • "para" tipos paramétricos de valores de tipo constante

Eles não estabelecem contextos polimórficos por si mesmos, mas ajudam a capacitar / simplificar o código dentro de tais contextos.

Você pode se sentir enganado ... não parece muito. A importância é que, em contextos polimórficos paramétricos (ou seja, dentro de modelos ou macros), estamos tentando dar suporte a um intervalo arbitrariamente grande de tipos, mas muitas vezes queremos expressar operações neles em termos de outras funções, literais e operações projetadas para um pequeno conjunto de tipos. Reduz a necessidade de criar funções ou dados quase idênticos por tipo quando a operação / valor é logicamente a mesma. Esses resources cooperam para adicionar uma atitude de "melhor esforço", fazendo o que é esperado intuitivamente usando as funções e dados disponíveis limitados e parando apenas com um erro quando há uma ambiguidade real.

Isso ajuda a limitar a necessidade de código polimórfico que suporte código polimórfico, criando uma rede mais restrita em torno do uso de polymorphism, de modo que o uso localizado não force o uso disseminado e disponibilize os benefícios do polymorphism conforme necessário, sem impor os custos de expor a implementação em tempo de compilation, ter várias cópias da mesma function lógica no código de object para suportar os tipos usados ​​e ao fazer o despacho virtual em oposição a inlining ou, pelo menos, chamadas resolvidas em tempo de compilation. Como é típico em C ++, o programador recebe muita liberdade para controlar os limites dentro dos quais o polymorphism é usado.

Em C ++, a distinção importante é a vinculação em tempo de execução versus tempo de compilation. Ad-hoc vs. paramétrico não ajuda muito, como explicarei mais adiante.

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

Nota – polymorphism em tempo de execução ainda pode ser resolvido em tempo de compilation, mas isso é apenas otimização. A necessidade de dar suporte à resolução em tempo de execução de maneira eficiente e negociar com outras questões é parte do que levou as funções virtuais a serem o que são. E isso é realmente fundamental para todas as formas de polymorphism em C ++ – cada uma delas surge de diferentes conjuntos de trade-offs feitos em um contexto diferente.

A sobrecarga de funções e a sobrecarga de operadores são a mesma coisa em todos os sentidos. Os nomes e a syntax para usá-los não afetam o polymorphism.

Os modelos permitem que você especifique muitas sobrecargas de funções de uma só vez.

Há outro conjunto de nomes para a mesma ideia de tempo de resolução …

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

Esses nomes são mais associados com OOP, por isso é um pouco estranho dizer que um modelo ou outra function não-membro usa binding inicial.

Para entender melhor a relação entre as funções virtuais e a sobrecarga de funções, também é útil entender a diferença entre “despacho único” e “despacho múltiplo”. A ideia pode ser entendida como uma progressão …

  • Primeiro, existem funções monomórficas. A implementação da function é identificada exclusivamente pelo nome da function. Nenhum dos parâmetros é especial.
  • Então, há um despacho único. Um dos parâmetros é considerado especial e usado (junto com o nome) para identificar qual implementação usar. Em OOP, tendemos a pensar neste parâmetro como “o object”, liste-o antes do nome da function, etc.
  • Então, há vários envios. Qualquer / todos os parâmetros contribuem para identificar qual implementação usar. Portanto, mais uma vez, nenhum dos parâmetros precisa ser especial.

Há obviamente mais para OOP do que uma desculpa para nomear um parâmetro como especial, mas isso é uma parte dele. E relacionando-me ao que eu disse sobre trade-offs – o despacho único é muito fácil de ser feito de forma eficiente (a implementação usual é chamada de “tabelas virtuais”). O envio múltiplo é mais complicado, não apenas em termos de eficiência, mas também para compilation separada. Se você está curioso, você pode procurar “o problema da expressão”.

Assim como é um pouco estranho usar o termo “binding inicial” para funções que não são membros, é um pouco estranho usar os termos “despacho único” e “envio múltiplo”, nos quais o polymorphism é resolvido em tempo de compilation. Geralmente, C ++ é considerado não ter vários dispatch, que é considerado um tipo específico de resolução de tempo de execução. No entanto, a sobrecarga de function pode ser vista como uma distribuição múltipla feita em tempo de compilation.

Voltando ao polymorphism paramétrico versus ad-hoc, esses termos são mais populares na functional programming e não funcionam bem em C ++. Mesmo assim…

O polymorphism paramétrico significa que você tem tipos como parâmetros e o mesmo código exato é usado independentemente do tipo usado para esses parâmetros.

O polymorphism ad-hoc é ad-hoc no sentido de que você fornece códigos diferentes dependendo dos tipos específicos.

Sobrecarga e funções virtuais são exemplos de polymorphism ad-hoc.

Mais uma vez, há alguns sinônimos …

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

Exceto que estes não são sinônimos, embora sejam comumente tratados como se fossem, e é aí que a confusão é provável que surja em C ++.

O raciocínio por trás do tratamento destes como sinônimos é que ao restringir o polymorphism a classs particulares de tipos, torna-se possível usar operações específicas para essas classs de tipos. A palavra “classs” aqui pode ser interpretada no sentido de OOP, mas na verdade apenas se refere a conjuntos (geralmente nomeados) de tipos que compartilham certas operações.

Assim, o polymorphism paramétrico é geralmente usado (pelo menos por padrão) para implicar polymorphism irrestrito. Como o mesmo código é usado independentemente dos parâmetros de tipo, as únicas operações suportáveis ​​são aquelas que funcionam para todos os tipos. Ao deixar o conjunto de tipos irrestritos, você limita severamente o conjunto de operações que pode aplicar a esses tipos.

Em eg Haskell, você pode ter …

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

Oa aqui é um tipo polimórfico irrestrito. Pode ser qualquer coisa, então não há muito que possamos fazer com valores desse tipo.

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

Aqui, a é restrito a ser um membro da class Num – tipos que agem como números. Essa restrição permite fazer coisas numéricas com esses valores, como adicioná-los. Até mesmo o 3 é polimórfico – a inferência de tipos descobre que você quer dizer o 3 do tipo a .

Eu penso nisso como polymorphism paramétrico restrito. Há apenas uma implementação, mas só pode ser aplicada em casos restritos. O aspecto ad-hoc é a escolha de quais + e 3 usar. Cada “instância” do Num tem sua própria implementação distinta. Então, mesmo em Haskell, “paramétricos” e “sem restrições” não são sinônimos – não me culpe, não é minha culpa!

Em C ++, tanto a sobrecarga quanto as funções virtuais são polymorphisms ad-hoc. A definição de polymorphism ad-hoc não se importa se a implementação é selecionada em tempo de execução ou em tempo de compilation.

O C ++ fica muito próximo do polymorphism paramétrico com modelos se cada parâmetro do modelo tiver o tipo nome do typename . Existem parâmetros de tipo e existe uma única implementação, independentemente dos tipos utilizados. No entanto, a regra “Falha de substituição não é um erro” significa que as restrições implícitas surgem como resultado do uso de operações dentro do modelo. Complicações adicionais incluem especialização de modelo para fornecer modelos alternativos – implementações diferentes (ad-hoc).

Então, de certa forma, o C ++ tem polymorphism paramétrico, mas é implicitamente restrito e pode ser substituído por alternativas ad-hoc – isto é, essa sorting não funciona realmente para C ++.

Quanto ao polymorphism ad-hoc, significa sobrecarga de funções ou sobrecarga do operador. Confira aqui:

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

Quanto ao polymorphism paramétrico, as funções do modelo também podem ser contadas porque elas não aceitam necessariamente os parâmetros dos tipos FIXED. Por exemplo, uma function pode classificar uma matriz de inteiros e também pode classificar uma matriz de strings, etc.

http://en.wikipedia.org/wiki/Parametric_polymorphism

Isso pode não ser de nenhuma ajuda, mas eu fiz isso para apresentar meus amigos para programar dando funções definidas, como START , e END para a function main, então não foi muito assustador (eles só usaram o arquivo main.cpp ). Ele contém classs e estruturas polimórficas, modelos, vetores, matrizes, diretivas de pré-processador, amizade, operadores e pointers (os quais você provavelmente deve saber antes de tentar o polymorphism):

Nota: Não está terminado, mas você pode ter a idéia

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; } } } }; 

Aqui está um exemplo básico usando classs 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 muitas formas, como tal, é usado para um operador agir de forma diferente em diferentes instâncias. O polymorphism é usado para implementar a inheritance. Por exemplo, definimos um fn draw () para uma forma de class e o draw fn pode ser implementado para desenhar um círculo, uma checkbox, um triângulo e outras formas. (que são objects da forma de class)

Se alguém disser CUT para essas pessoas

 The Surgeon The Hair Stylist The Actor 

O que vai acontecer?

 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. 

Então, a representação acima mostra o que é polymorphism (mesmo nome, comportamento diferente) em OOP.

Se você está indo para uma entrevista e entrevistador pede que você diga / mostre um exemplo ao vivo de polymorphism na mesma sala em que estamos sentados, digamos

Answer – Door / Windows

Wondering How?

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.