Considere o código a seguir:
#include <iostream>
using namespace std;
class Animal {
public:
    virtual void falar() { cout << "O animal faz um som\n"; }
};
class Cachorro : public Animal {
public:
    void falar() override { cout << "Au Au!\n"; }
};
int main() {
    Animal *a = new Cachorro();
    a->falar();
    delete a;
}
Qual será a saída do programa?
a) \quad \mathrm{O} animal faz um som
b) Au Au !
c) Nenhuma saída
d) Erro de compilação
e) \quad \mathrm{O} animal faz um som / \mathrm{Au} \mathrm{Au} !

Passo 1

O enunciado nos dá um programa C++ com duas classes e pede qual será a saída entre as opções dadas. Informações relevantes dadas no enunciado:
  • A classe base Animal tem um método público falar() declarado como virtual e que faz cout << "O animal faz um som\n";.
  • A classe derivada Cachorro herda de Animal e sobrescreve (override) o método falar() para fazer cout << "Au Au!\n";.
  • Em main() há a linha Animal *a = new Cachorro(); (um ponteiro para Animal que aponta para um objeto Cachorro), em seguida a->falar(); e depois delete a;.
  • As alternativas de resposta são:
    a) O animal faz um som
    b) Au Au !
    c) Nenhuma saída
    d) Erro de compilação
    e) O animal faz um som / Au Au !

Passo 2

Entendendo o que faz virtual num método (aula rápida e prática). Quando um método de uma classe é marcado como virtual, isso indica ao compilador que chamadas a esse método através de um ponteiro ou referência para a classe base devem ser resolvidas em tempo de execução (dynamic dispatch), consultando o tipo real do objeto apontado. Em termos simples: se você tem um Animal* que aponta para um Cachorro, e falar() é virtual, então a->falar() vai chamar Cachorro::falar() (o método da classe derivada), não Animal::falar().

Passo 3

Entendendo override. A palavra-chave override é um auxílio para o compilador: ela diz “este método pretende sobrescrever um método virtual da classe base”. Se a assinatura não corresponder a nenhum método virtual da base, o compilador gera erro. Aqui void falar() tem a mesma assinatura em Animal e em Cachorro, portanto Cachorro::falar() override é válido e garante que estamos realmente sobrescrevendo.

Passo 4

Aplicando ao código: linha a linha relevante.
  • Animal *a = new Cachorro(); cria dinamicamente um objeto do tipo Cachorro e armazena seu endereço em um ponteiro para Animal. O tipo estático do ponteiro é Animal*, o tipo dinâmico do objeto é Cachorro.
  • a->falar(); — como falar() é virtual, a chamada é feita dinamicamente. O runtime verifica o tipo real do objeto apontado (que é Cachorro) e invoca Cachorro::falar(). Logo, será impresso o texto do Cachorro.
  • O cout no Cachorro::falar() emite Au Au! seguido de \n (nova linha). Portanto a saída no terminal será exatamente Au Au! numa linha.

Passo 5

Sobre delete a; — vale comentar: deletar através de um ponteiro para base quando a base não tem destrutor virtual pode causar comportamento indefinido em C++ se o destrutor da base não for virtual. No entanto, isso não afeta a chamada já realizada a falar() (ela já ocorreu antes do delete). Como a questão é sobre a saída do programa (o que foi impresso), a ação de delete não impede que Au Au! tenha sido impresso. Em tarefas de prova costuma-se assumir que o foco é a resolução dinâmica do método falar() e que não há efeitos colaterais relevantes aqui.

Passo 6

Conclusão direta: a chamada a->falar(); resolve para o método da classe derivada Cachorro, que imprime Au Au!. Não há razão para erro de compilação (assinaturas batem e override é correto) e o programa produz saída (não é nenhuma das opções “nenhuma saída” ou “erro de compilação”). Também não é impresso primeiro a mensagem da base e depois a do derivado; apenas o método sobrescrito é chamado.

Resposta

b) Au Au !

Exercícios relacionados