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)
animal faz um som
b) Au Au !
c) Nenhuma saída
d) Erro de compilação
e)
animal faz um som
!
#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;
}
a)
b) Au Au !
c) Nenhuma saída
d) Erro de compilação
e)
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
Animaltem um método públicofalar()declarado comovirtuale que fazcout << "O animal faz um som\n";. - A classe derivada
Cachorroherda deAnimale sobrescreve (override) o métodofalar()para fazercout << "Au Au!\n";. - Em
main()há a linhaAnimal *a = new Cachorro();(um ponteiro paraAnimalque aponta para um objetoCachorro), em seguidaa->falar();e depoisdelete 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 tipoCachorroe armazena seu endereço em um ponteiro paraAnimal. O tipo estático do ponteiro éAnimal*, o tipo dinâmico do objeto éCachorro.a->falar();— comofalar()évirtual, a chamada é feita dinamicamente. O runtime verifica o tipo real do objeto apontado (que éCachorro) e invocaCachorro::falar(). Logo, será impresso o texto doCachorro.- O
coutnoCachorro::falar()emiteAu Au!seguido de\n(nova linha). Portanto a saída no terminal será exatamenteAu 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 !