C++: Polimorfismo

Última atualização: 2026-08-26

Na Aula 31, aprendemos sobre herança — permitindo que classes derivadas reutilizem código da classe base.

Mas em cenários do mundo real, você precisa usar um ponteiro da classe base para tratar diferentes objetos de classes derivadas de forma uniforme.

Isso é polimorfismo — "uma interface, múltiplas implementações."


1. O que é Polimorfismo?

(1) 1.1 Polimorfismo no Dia a Dia

Exemplo do Dia a Dia Equivalente no Programa
Você manda um cachorro "falar", ele late Chamar speak() em um ponteiro da classe base, mas executando a versão da classe derivada
Você manda um gato "falar", ele mia A mesma chamada de função produz comportamento diferente para objetos diferentes

A essência do polimorfismo: Usar um ponteiro da classe base para apontar para um objeto da classe derivada, de modo que chamar uma função virtual vincula dinamicamente à implementação da classe derivada.

(2) 1.2 Por que Precisamos de Polimorfismo?

Sem polimorfismo (tedioso):

▶ Exemplo 2: Demonstração de Programação Orientada a Objetos (Dificuldade ⭐)

CPP
#include <iostream>

class Dog {
public:
 void speak() { std::cout << "Au!" << std::endl; }
};

class Cat {
public:
 void speak() { std::cout << "Miau!" << std::endl; }
};

int main() {
 Dog* d = new Dog();
 Cat* c = new Cat();
 
 d->speak();
 c->speak();
 // ❌ Precisa tratar tipos diferentes separadamente
 
 return 0;
}
▶ Experimente

Saída:

TEXT 📖 Somente leitura
Au!
Miau!

Com polimorfismo (conciso):

CPP
#include <iostream>

class Animal {
public:
 virtual void speak() { std::cout << "Animal fala" << std::endl; }
};

class Dog : public Animal {
public:
 void speak() override { std::cout << "Au!" << std::endl; }
};

class Cat : public Animal {
public:
 void speak() override { std::cout << "Miau!" << std::endl; }
};

int main() {
 Animal* animals = {new Dog(), new Cat()};
 
 for (int i = 0; i < 2; i++) {
 animals[i]->speak(); // ✅ Polimorfismo: chama automaticamente a versão correta
 }
 
 return 0;
}


2. Funções Virtuais (virtual)

(1) 2.1 Uso Básico

Declare uma função como virtual na classe base, e faça override na classe derivada.

CPP
#include <iostream>
#include <string>

class Animal {
protected:
 std::string name;
 
public:
 Animal(const std::string& n) : name(n) {}
 
 // Declarar como função virtual
 virtual void speak() {
 std::cout << name << " faz um som." << std::endl;
 }
 
 // A classe base também deve ter um destrutor virtual!
 virtual ~Animal() {}
};

class Dog : public Animal {
public:
 Dog(const std::string& n) : Animal(n) {}
 
 void speak() override {
 std::cout << name << " diz: Au!" << std::endl;
 }
};

int main() {
 Animal* a = new Dog("Buddy");
 a->speak(); // ✅ Saída: Buddy diz: Au!
 
 delete a; // ✅ Chamará o destrutor de Dog primeiro, depois o de Animal
 return 0;
}

⚠️ Ponto Importante: Quando uma classe base tem funções virtuais, o destrutor também deve ser declarado virtual! Caso contrário, delete em um ponteiro da classe base não chamará o destrutor da classe derivada.


(2) 2.2 A Palavra-chave override (C++11)

O C++11 introduziu a palavra-chave override, que permite ao compilador verificar se você está realmente sobrescrevendo uma função virtual.

CPP
class Dog : public Animal {
public:
 // ✅ Com override, o compilador verificará
 void speak() override { ... }
 
 // ❌ Se a assinatura estiver errada (ex.: void speak(int x)), o compilador dará erro
 // void speak(int x) override { ... } // Erro de compilação: nenhuma função para sobrescrever
};

💡 Recomendação: Sempre use override — deixe o compilador capturar bugs para você.



3. Funções Virtuais Puras e Classes Abstratas

(1) 3.1 O que é uma Função Virtual Pura?

Uma função virtual pura é uma função virtual que é apenas declarada mas não implementada na classe base, marcada com = 0.

CPP
class Animal {
public:
 // Função virtual pura
 virtual void speak() = 0;
 
 virtual ~Animal() {}
};

Uma classe com funções virtuais puras é chamada de "classe abstrata" — não pode ser instanciada e só pode servir como classe base.

CPP
Animal a; // ❌ Erro de compilação: não é possível instanciar classe abstrata
Animal* p = new Dog("Buddy"); // ✅ Pode usar um ponteiro da classe base para apontar para uma classe derivada

(2) 3.2 Por que Precisamos de Funções Virtuais Puras?

Para forçar as classes derivadas a sobrescreverem a função.

CPP
class Animal {
public:
 virtual void speak() = 0; // Forçar classes derivadas a implementar
};

class Dog : public Animal {
public:
 void speak() override {
 std::cout << "Au!" << std::endl;
 }
 // ✅ Se você esquecer de sobrescrever speak(), o compilador dará erro
};


4. Como as Funções Virtuais Funcionam (Opcional)

(1) 4.1 Tabela de Funções Virtuais (vtable)

C++ implementa polimorfismo usando uma tabela de funções virtuais (vtable):

  1. Para classes com funções virtuais, o compilador gera uma vtable (uma tabela que armazena endereços de funções virtuais)
  2. Cada objeto contém um vptr oculto (um ponteiro para a vtable)
  3. Quando uma função virtual é chamada, o endereço correto da função é encontrado através do vptr

💡 Dica: É por isso que objetos de classes com funções virtuais ocupam espaço extra de um ponteiro (8 bytes em sistemas de 64 bits).



5. Prática: Cálculo de Área de Formas Geométricas

▶ Exemplo 1: Usando Polimorfismo para Calcular Áreas de Diferentes Formas (Dificuldade ⭐⭐)

CPP 📖 Somente leitura
#include <iostream>
#include <vector>
#include <cmath>

// Classe base abstrata
class Shape {
public:
 virtual double area() = 0;
 virtual void printInfo() = 0;
 virtual ~Shape() {}
};

// Círculo
class Circle : public Shape {
private:
 double radius;
 
public:
 Circle(double r) : radius(r) {}
 
 double area() override {
 return 3.14159 * radius * radius;
 }
 
 void printInfo() override {
 std::cout << "Círculo, raio = " << radius 
 << ", área = " << area() << std::endl;
 }
};

// Retângulo
class Rectangle : public Shape {
private:
 double width, height;
 
public:
 Rectangle(double w, double h) : width(w), height(h) {}
 
 double area() override {
 return width * height;
 }
 
 void printInfo() override {
 std::cout << "Retângulo, " << width << " x " << height 
 << ", área = " << area() << std::endl;
 }
};

int main() {
 std::vector<Shape*> shapes;
 shapes.push_back(new Circle(5.0));
 shapes.push_back(new Rectangle(4.0, 6.0));
 
 for (Shape* s : shapes) {
 s->printInfo();
 }
 
 // Liberar memória
 for (Shape* s : shapes) {
 delete s;
 }
 
 return 0;
}
44 linhas de lógica (limite de 40, somente leitura)

Saída:

TEXT 📖 Somente leitura
Círculo, raio = 
Retângulo,  x 

❓ Perguntas Frequentes

P: Por que o destrutor deve ser virtual se há funções virtuais? R: Se você fizer delete em um ponteiro da classe base apontando para um objeto da classe derivada, e o destrutor da classe base não for virtual, apenas o destrutor da classe base será chamado, causando vazamento de recursos na classe derivada.

CPP
Animal* a = new Dog("Buddy");
delete a; // ❌ Se ~Animal() não for virtual, o destrutor de Dog não será chamado

Regra de Ouro: Se uma classe base tem funções virtuais, ~BaseClass() deve ser virtual.

P: Qual a diferença entre override e final? R: > - override: diz ao compilador que esta função sobrescreve uma função virtual da classe base (deixa o compilador verificar para você) > - final: diz ao compilador que esta função não pode ser sobrescrita (usada em classes derivadas)

CPP
class Dog : public Animal {
public:
void speak() override final { ... } // final: classes derivadas não podem mais sobrescrever speak()
};

P: Quando devo usar polimorfismo? R: Quando precisar tratar diferentes tipos de objetos através de uma interface unificada.

Cenários típicos:

  • Jogos: todos os personagens herdam de Character, gerenciados uniformemente com Character*
  • GUI: todos os controles herdam de Widget, desenhados uniformemente com Widget*
  • Sistemas de arquivos: todos os arquivos herdam de File, lidos/escritos uniformemente com File*

▶ Exemplo 3: Polimorfismo com Função Virtual (Dificuldade ⭐)

CPP
#include <iostream>
#include <string>

class Shape {
public:
    virtual double area() { return 0; }
    virtual ~Shape() {}
};

class Circle : public Shape {
    double radius;
public:
    Circle(double r) : radius(r) {}
    double area() override { return 3.14 * radius * radius; }
};

int main() {
    Shape* s = new Circle(2.0);
    std::cout << "Produto de superfície: " << s->area() << std::endl;
    delete s;

    return 0;
}
▶ Experimente

Saída:

TEXT 📖 Somente leitura
Produto de superfície: 
💡 Dica: Use virtual para declarar funções virtuais e override para indicar explicitamente a sobrescrita. Um ponteiro da classe base chamando um método da classe derivada é polimorfismo.



📖 Resumo

📝 Exercícios

  1. Básico (Dificuldade ⭐): Defina uma classe base Vehicle com uma função virtual pura void move(). Faça Car e Bicycle herdarem dela, exibindo "O carro está dirigindo na estrada" e "A bicicleta está pedalando na ciclovia" respectivamente.

  2. Intermediário (Dificuldade ⭐⭐): Estenda o programa acima adicionando uma função virtual void stop(), com Car exibindo "O carro encosta" e Bicycle exibindo "A bicicleta freia".

  3. Desafio (Dificuldade ⭐⭐⭐): Projete um sistema de salário de funcionários:

  4. Classe base abstrata Employee (nome, número de funcionário, virtual double calcSalary() = 0)

  5. FullTimeEmployee (salário mensal)

  6. PartTimeEmployee (salário por hora × horas trabalhadas)

  7. Use vector<Employee*> para armazenar todos os funcionários e chame calcSalary() uniformemente

  8. Funções virtuais: declaradas na classe base, sobrescritas na classe derivada, polimorfismo em tempo de execução


6. 🚀 Próximos Passos

Agora que você aprendeu polimorfismo, vamos avançar para Templates (Aula 33) — fazendo funções e classes suportarem tipos arbitrários para verdadeira "programação genérica"!

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%