C++ Polimorfismo com Exemplo

⚡ Resumo Inteligente

Polimorfismo em C++ Permite que uma única função membro assuma várias formas, comportando-se de maneira diferente dependendo do objeto que a invoca. Baseia-se em herança e aparece em duas variantes, resolvidas em tempo de compilação ou em tempo de execução.

  • 🔷 Definição: Polimorfismo significa "muitas formas", permitindo que uma função membro se comporte de maneira diferente dependendo do objeto que a chama.
  • 🧭 Dois tipos: C++ Oferece polimorfismo em tempo de compilação (estático) e polimorfismo em tempo de execução (dinâmico).
  • 🧩 Tempo de compilação: A sobrecarga de funções e a sobrecarga de operadores selecionam a função correta durante a compilação.
  • 🔁 Tempo de execução: A sobrescrita de funções e as funções virtuais resolvem a chamada durante a execução do programa.
  • ⚙️ Funções virtuais: A palavra-chave `virtual` em uma classe base permite a vinculação tardia por meio de um ponteiro para a classe base.
  • 🤖 Assistência de IA: O GitHub Copilot e assistentes semelhantes criam funções sobrecarregadas, substituídas e virtuais a partir de um breve comentário.

C++ Polimorfismo

O que é polimorfismo em C++?

In C++O polimorfismo faz com que uma função membro se comporte de maneira diferente dependendo do objeto que a chama/invoca. Polimorfismo é uma palavra grega que significa ter muitas formas. Ele ocorre quando você tem uma hierarquia de funções membro. aulas relacionados por herança.

Por exemplo, suponha que temos a função makeSound(). Quando um gato chama esta função, ele produz o som de miado. Quando uma vaca invoca a mesma função, ela emitirá o som moow.

Polimorfismo em C++ Exemplo makeSound

Embora tenhamos uma função, ela se comporta de maneira diferente em circunstâncias diferentes. A função tem vários formatos; portanto, alcançamos o polimorfismo.

Com a ideia central definida, C++ Organiza esse comportamento em duas categorias distintas.

Tipos de polimorfismo

C++ suporta dois tipos de polimorfismo:

  • Polimorfismo em tempo de compilação e
  • Polimorfismo de tempo de execução.

Tipos de polimorfismo em C++

A primeira categoria resolve todas as chamadas de função antes mesmo de o programa ser executado.

Polimorfismo de tempo de compilação

Você invoca as funções sobrecarregadas combinando o número e o tipo de argumentos. A informação está presente durante o tempo de compilação. Isto significa o C++ o compilador selecionará a função correta em tempo de compilação.

O polimorfismo em tempo de compilação é obtido por meio da sobrecarga de funções e da sobrecarga de operadores.

Sobrecarga de função

A sobrecarga de funções ocorre quando temos muitas funções com nomes semelhantes, mas argumentos diferentes. Os argumentos podem diferir em termos de número ou tipo.

Exemplo 1

#include <iostream> 
using namespace std;

void test(int i) {
	cout << " The int is " << i << endl;
}
void test(double  f) {
	cout << " The float is " << f << endl;
}
void test(char const *ch) {
	cout << " The char* is " << ch << endl;
}

int main() {
	test(5);
	test(5.5);
	test("five");
	return 0;
}

Saída:

C++ Sobrecarga de função Exemplo 1 saída

Aqui está uma captura de tela do código:

C++ Sobrecarga de funções Exemplo 1 código

Code Explicação:

  1. Inclua o arquivo de cabeçalho iostream em nosso código. Poderemos usar suas funções.
  2. Inclua o namespace std em nosso código. Poderemos usar suas classes sem chamá-lo.
  3. Crie uma função chamada test que receba um parâmetro inteiro i. O {marca o início do corpo do teste de função.
  4. Instrução a ser executada se o teste de função acima for invocado/chamado.
  5. Fim do corpo do teste de função acima.
  6. Crie uma função chamada test que receba um parâmetro flutuante f. O {marca o início do corpo do teste de função.
  7. Instrução a ser executada se o teste de função acima for invocado/chamado.
  8. Fim do corpo do teste de função acima.
  9. Crie uma função chamada test que receba um parâmetro de caractere ch. O {marca o início do corpo do teste de função.
  10. Instrução a ser executada se o teste de função acima for invocado/chamado.
  11. Fim do corpo do teste de função acima.
  12. Chame a função main(). O { marca o início do corpo da função.
  13. Chame a função test e passe 5 para ela como o valor do argumento. Isto invoca a função de teste que aceita um argumento inteiro, ou seja, a primeira função de teste.
  14. Chame a função test e passe 5.5 para ela como o valor do argumento. Isto invocará a função de teste que aceita um argumento float, ou seja, a segunda função de teste.
  15. Chame a função test e passe cinco para ela como o valor do argumento. Isto invocará a função de teste que aceita um argumento de caractere, ou seja, a terceira função de teste.
  16. O programa deverá retornar um valor se for executado com sucesso.
  17. O final do corpo da função main().

Temos três funções com o mesmo nome, mas diferentes tipos de argumentos. Alcançamos o polimorfismo.

Operasobrecarga

In Operator Sobrecarga, definimos um novo significado para um C++ operadorIsso também altera o funcionamento do operador. Por exemplo, podemos definir o operador + para concatenar duas strings. Conhecemos esse operador como o operador de adição para somar valores numéricos. Após nossa definição, quando colocado entre números inteiros, ele os somará. Quando colocado entre strings, ele as concatenará.

Exemplo 2

#include<iostream> 
using namespace std;

class ComplexNum {
private:
	int real, over;
public:
	ComplexNum(int rl = 0, int ov = 0) {
		real = rl;   
		over = ov; 
	}

	ComplexNum operator + (ComplexNum const &obj) {
		ComplexNum result;
		result.real = real + obj.real;
		result.over = over + obj.over;
		return result;
	}
	void print() { 
		cout << real << " + i" << over << endl; 
	}
};
int main()
{
	ComplexNum c1(10, 2), c2(3, 7);
	ComplexNum c3 = c1+c2;
	c3.print();
}

Saída:

C++ sobrecarga de operadores Exemplo 2 saída

Aqui está uma captura de tela do código:

C++ Sobrecarga de operadores Exemplo 2 código

C++ sobrecarga do operador Exemplo 2 resultado

Code Explicação:

  1. Inclua o arquivo de cabeçalho iostream em nosso programa para usar suas funções.
  2. Inclua o namespace std em nosso programa para usar suas classes sem chamá-lo.
  3. Crie uma classe chamada ComplexNum. O { marca o início do corpo da classe.
  4. Use o modificador de acesso privado para marcar variáveis ​​como privadas, o que significa que elas só podem ser acessadas dentro da classe.
  5. Defina duas variáveis ​​inteiras, reais e superiores.
  6. Use o modificador de acesso `public` para marcar o construtor como público, o que significa que ele será acessível mesmo de fora da classe.
  7. Crie o construtor da classe e inicialize as variáveis.
  8. Inicialize o valor da variável real.
  9. Inicialize o valor da variável.
  10. Fim do corpo do construtor.
  11. Precisamos substituir o significado do operador +.
  12. Crie o tipo de dados resultado do tipo ComplexNum.
  13. Use o operador + com números complexos. Esta linha adicionará a parte real de um número à parte real de outro número.
  14. Use o operador + com números complexos. Esta linha adicionará a parte imaginária de um número à parte imaginária de outro número.
  15. O programa retornará o valor da variável resultado após a execução bem-sucedida.
  16. Fim da definição do novo significado do operador +, ou seja, sobrecarga.
  17. Chame o método print().
  18. Imprima o novo número complexo após a adição no console.
  19. Fim do corpo da função print().
  20. Fim do corpo da classe ComplexNum.
  21. Chame a função main().
  22. Passe os valores das partes reais e complexas a serem somadas. A primeira parte de c1 será somada à primeira parte de c2, ou seja, 10+3. A segunda parte de c1 será somada à segunda parte de c, ou seja, 2+7.
  23. Execute uma operação usando o operador + sobrecarregado e armazenando o resultado na variável c3.
  24. Imprima o valor da variável c3 no console.
  25. Fim do corpo da função main().

Enquanto o polimorfismo em tempo de compilação decide cedo, a segunda categoria adia a escolha até a execução.

Polimorfismo de tempo de execução

Isso acontece quando o método de um objeto é invocado/chamado durante o tempo de execução, e não durante o tempo de compilação. O polimorfismo de tempo de execução é obtido por meio da substituição de funções. A função a ser chamada/invocada é estabelecida durante o tempo de execução.

Substituição de função

A substituição de função ocorre quando uma função da classe base recebe uma nova definição em uma classe derivada. Nesse momento, podemos dizer que a função base foi substituída.

Por exemplo:

#include <iostream>
using namespace std;
class Mammal {

public:
	void eat() {

		cout << "Mammals eat...";
	}

};

class Cow: public Mammal {

public:
	void eat() {

		cout << "Cows eat grass...";
	}
};
int main(void) {

	Cow c = Cow();

	c.eat();

	return 0;

}

Saída:

C++ saída de substituição de função

Aqui está uma captura de tela do código:

C++ código de substituição de função

Code Explicação:

  1. Importe o arquivo de cabeçalho iostream para nosso programa para usar suas funções.
  2. Inclua o namespace std em nosso programa para usar suas classes sem chamá-lo.
  3. Crie uma classe chamada Mamífero. O { marca o início do corpo da classe.
  4. Use o modificador de acesso público para definir a função que estamos prestes a criar como acessível publicamente. Será acessível de fora desta classe.
  5. Crie uma função pública chamada comer. O { marca o início do corpo da função.
  6. Imprima a instrução adicionada à função cout quando a função eat() é invocada.
  7. O final do corpo da função eat().
  8. Fim do corpo da classe Mamífero.
  9. Crie uma classe chamada Cow que herda a classe Mammal. Cow é a classe derivada, enquanto Mammal é a classe base. O { marca o início desta aula.
  10. Use o modificador de acesso público para marcar a função que estamos prestes a criar como acessível publicamente. Será acessível de fora desta classe.
  11. Substitua a função eat() que foi definida na classe base. O { marca o início do corpo da função.
  12. A instrução a ser impressa no console quando esta função é invocada.
  13. Fim do corpo da função comer().
  14. Fim do corpo da classe Vaca.
  15. Chame a função main(). O { marca o início do corpo desta função.
  16. Crie uma instância da classe Cow e dê a ela o nome c.
  17. Chame a função eat() definida na classe Cow.
  18. O programa deve retornar um valor após a conclusão bem-sucedida.
  19. Fim da função main().

C++ Função Virtual

Uma função virtual é outra maneira de implementar o polimorfismo em tempo de execução em C++. É uma função especial definida em uma classe base e redefinida na classe derivada. Para declarar uma função virtual, você deve usar a palavra-chave virtual. A palavra-chave deve preceder a declaração da função na classe base.

Se uma classe de função virtual for herdada, a classe virtual redefine a função virtual para atender às suas necessidades. Por exemplo:

#include <iostream>  
using namespace std;
class ClassA {
		public:
		virtual void show() {
			cout << "The show() function in base class invoked..." << endl;
		}
	};
	class ClassB :public ClassA {
	public:
		void show() 	{
			cout << "The show() function in derived class invoked...";
		}
	};
	int main() {
		ClassA* a;   
		ClassB b;
		a = &b;
		a->show();      
	}

Saída:

C++ saída da função virtual

Aqui está uma captura de tela do código:

C++ código de função virtual

Code Explicação:

  1. Inclua o arquivo de cabeçalho iostream no código para usar suas funções.
  2. Inclua o namespace std em nosso código para usar suas classes sem chamá-lo.
  3. Crie uma classe chamada ClassA.
  4. Use o modificador de acesso público para marcar um membro da classe como acessível publicamente.
  5. Crie uma função virtual chamada show(). Será uma função pública.
  6. O texto a ser impresso quando show() invocado é invocado. O final é um C++ palavra-chave, que significa linha final. Move o cursor do mouse para a próxima linha.
  7. Fim do corpo da função virtual show().
  8. Fim do corpo da classe ClassA.
  9. Criando uma nova classe chamada ClassB que herda a classe ClassA. ClassA se torna a classe base enquanto ClassB se torna a classe derivada.
  10. Use o modificador de acesso público para marcar um membro da classe como acessível publicamente.
  11. Redefina a função virtual show() derivada na classe base.
  12. O texto a ser impresso no console quando a função show() definida na classe derivada é invocada.
  13. Fim do corpo da função show().
  14. Fim do corpo da classe derivada, ClassB.
  15. Chame a função main(). A lógica do programa deve ser adicionada ao seu corpo.
  16. Crie uma variável de ponteiro chamada a. Ele aponta para a classe chamada ClassA.
  17. Crie uma instância da classe chamada ClassB. A instância recebe o nome b.
  18. Atribua os valores armazenados no endereço b na variável a.
  19. Invoque a função show() definida na classe derivada. A vinculação tardia foi implementada.
  20. Fim do corpo da função main().

Com ambos os mecanismos explicados, a tabela abaixo os compara lado a lado.

Polimorfismo em tempo de compilação vs. Polimorfismo em tempo de execução

Aqui estão as principais diferenças entre os dois:

Polimorfismo em tempo de compilação Polimorfismo em tempo de execução
Também é chamado de ligação precoce ou polimorfismo estático. Também é chamado de ligação tardia/dinâmica ou polimorfismo dinâmico.
O método é chamado/invocado durante o tempo de compilação O método é chamado/invocado durante o tempo de execução
Implementado por meio de sobrecarga de função e sobrecarga de operador Implementado por meio de substituição de método e funções virtuais
Exemplo, sobrecarga de método. Muitos métodos podem ter nomes semelhantes, mas diferentes números ou tipos de argumentos Exemplo, substituição de método. Muitos métodos podem ter um nome semelhante e o mesmo protótipo.
Execução mais rápida, pois a descoberta de métodos é feita durante a compilação. Execução mais lenta, pois a descoberta de métodos é feita em tempo de execução.
Less flexibilidade para resolução de problemas é fornecida, pois tudo é conhecido durante o tempo de compilação. Muita flexibilidade é fornecida para resolver problemas complexos, uma vez que os métodos são descobertos durante o tempo de execução.

Perguntas Frequentes

A sobrecarga define várias funções com o mesmo nome, mas parâmetros diferentes, resolvidas em tempo de compilação sem herança. A sobrescrita redefine uma função herdada da classe base com a mesma assinatura em uma classe derivada, resolvida em tempo de execução quando a função é declarada como virtual.

Uma função virtual pura é declarada com `virtual` = 0 e não possui corpo na classe base. Qualquer classe que contenha uma torna-se uma função absoluta.tracClasse t, que não pode ser instanciada diretamente e força as classes derivadas a fornecerem sua própria implementação.

Quando uma classe declara funções virtuais, o compilador constrói uma tabela virtual (vtable) de ponteiros para funções. Cada objeto armazena um ponteiro oculto para a vtable de sua classe, de modo que um ponteiro da classe base chama a função sobrescrita correta em tempo de execução por meio de vinculação tardia.

Sim. Os templates permitem que uma função ou classe opere com diversos tipos de dados, com o compilador gerando código específico para cada tipo durante a compilação. Essa programação genérica é uma forma de polimorfismo estático que complementa a sobrecarga de funções e a sobrecarga de operadores.

Ao excluir um objeto derivado por meio de um ponteiro para a classe base, somente um destrutor virtual garante que o destrutor do objeto derivado também seja executado. Sem ele, a limpeza fica incompleta e causa vazamentos de memória. Declarar o destrutor da classe base como virtual faz com que a destruição se comporte de forma polimórfica.

O polimorfismo permite que uma única interface lide com vários tipos de objetos, reduzindo a duplicação de código e simplificando a manutenção. Ele suporta designs flexíveis e extensíveis, nos quais novas classes derivadas são integradas sem alterar o código existente, e possibilita padrões poderosos construídos com base em ponteiros e referências de classes base.

Sim. Os assistentes de codificação com IA transformam um comentário curto em funções sobrecarregadas, classes base virtuais e sobrescritas derivadas. Eles lidam rapidamente com código repetitivo, embora você ainda deva verificar palavras-chave virtuais, assinaturas de funções e a segurança do destrutor antes de compilar o código gerado.

Sim. Travas deslizantes portáteis Copiloto do GitHub Sugere declarações de funções virtuais, sobrescritas de classes derivadas e sobrecargas de operadores conforme você digita. Isso acelera a geração de código repetitivo para hierarquias de herança, embora você deva confirmar a palavra-chave `virtual`, as assinaturas correspondentes e o comportamento correto de vinculação tardia antes de compilar.

Resuma esta postagem com: