#include <iostream>
using namespace std;
class Pessoa {
public:
    virtual void falar() { cout << "Sou uma pessoa." << endl; }
};
class Estudante : public Pessoa {
public:
    void falar() override { cout << "Sou um estudante." << endl; }
};
int main() {
    Pessoa* p = new Estudante();
    p->falar();
}
O que será impresso?

Passo 1

O enunciado pede: “O que será impresso?” e nos dá o código C++ abaixo. Informações relevantes extraídas do enunciado:
  • Há uma classe Pessoa com método público virtual void falar() que faz cout << "Sou uma pessoa." << endl;.
  • Há uma classe Estudante que herda public Pessoa e que implementa void falar() override fazendo cout << "Sou um estudante." << endl;.
  • No main é criada uma variável Pessoa* p = new Estudante(); — ou seja, um ponteiro para Pessoa que aponta para um objeto Estudante.
  • Em seguida é chamada p->falar();.
    A pergunta é: qual linha aparecerá na saída quando o programa for executado?

Passo 2

Entender o que vai acontecer exige saber o que significa que o método em Pessoa é virtual e que Estudante override esse método. Vou explicar isso devagar, passo a passo, como se fosse um truque de mágica que o compilador faz por trás das cortinas.
  • Quando um método é marcado como virtual em C++, o compilador cria para cada objeto (na prática, para cada tipo com métodos virtuais) uma tabela interna de funções (chamada de vtable) e coloca no objeto um ponteiro para essa tabela (vptr). Essa tabela diz “se alguém chamar este método virtual neste objeto, chame esta função concreta”.
  • O importante: a ligação entre a chamada do método e a função concreta que será executada é feita em tempo de execução (runtime), não apenas em tempo de compilação. Isso é chamado de ligação dinâmica (dynamic dispatch) ou polimorfismo.
  • A palavra-chave override em Estudante só serve para garantir ao compilador que você está realmente substituindo (overriding) um método virtual da base; se houver erro de assinatura, o compilador avisa. Mas o comportamento de chamada em tempo de execução vem do virtual na classe base.

Passo 3

Agora apliquei isso ao código concreto:
  • Pessoa* p = new Estudante(); cria um objeto cujo tipo real (dinâmico) é Estudante, mas a variável que o referencia tem tipo estático Pessoa*.
  • Quando fazemos p->falar();, por causa de falar() ser virtual na classe base, o mecanismo de tempo de execução olha na vtable do objeto real (que é um Estudante) e chama a implementação correspondente do Estudante.
  • Se falar() não tivesse sido virtual em Pessoa, então a chamada p->falar() usaria ligação estática e chamaria Pessoa::falar(). Mas como é virtual, o método do objeto derivado é chamado.

Passo 4

O método que será executado é, portanto, o de Estudante:
  • Estudante::falar() executa cout << "Sou um estudante." << endl;.
  • endl termina a linha e esvazia o buffer de saída, então a saída que aparece no terminal é exatamente a linha Sou um estudante. seguida de quebra de linha.

Passo 5

Resumo prático e dica para prova (estou torcendo por você!): quando tiver uma variável de tipo base apontando para um objeto derivado e o método for virtual na base, a chamada vai para a versão do derivado. É o comportamento de polimorfismo que vale na hora da prova. Se quiser praticar rápido: remova virtual e veja que a saída muda para a versão da classe base — é uma diferença simples de testar para fixar a ideia.

Resposta

Sou um estudante.

Exercícios relacionados