Sobrescrita de métodos
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 51 de 104.
Sobrescrita de método ocorre quando uma classe derivada fornece sua própria implementação de um método que já existe na classe base. Isso permite que as classes derivadas personalizem o comportamento herdado, mantendo a mesma assinatura de método.
Para sobrescrever um método, basta definir um método na classe derivada com exatamente o mesmo nome, tipo de retorno e parâmetros:
class Animal {
public:
void speak() {
std::cout << "Some sound" << std::endl;
}
};
class Dog : public Animal {
public:
void speak() {
std::cout << "Woof!" << std::endl;
}
};
Dog d;
d.speak(); // Saída: Woof!A versão da classe derivada oculta a versão da classe base. No entanto, você ainda pode acessar o método da classe base usando o operador de resolução de escopo:
class Dog : public Animal {
public:
void speak() {
Animal::speak(); // Chama a versão da classe base
std::cout << "Woof!" << std::endl;
}
};Existe uma limitação importante com esta forma básica de sobrescrita. Quando você acessa um objeto derivado através de um ponteiro ou referência da classe base, a versão da classe base é chamada em vez disso:
Dog d;
Animal* ptr = &d;
ptr->speak(); // Saída: Some sound (não Woof!)Isso acontece porque o compilador determina qual método chamar com base no tipo do ponteiro, não no tipo real do objeto. Para alcançar o verdadeiro polimorfismo de tempo de execução, onde o método correto é chamado com base no objeto real, você precisará de funções virtuais, que abordaremos na próxima lição.
Desafio
FácilVamos construir um sistema de notificações que demonstra a sobrescrita de métodos na prática. Você criará uma classe base Notifier e dois notificadores especializados que personalizam a forma como as mensagens são entregues.
Você organizará seu código em três arquivos:
Notifier.h: Defina uma classe baseNotifiercom:- Um membro
std::string recipientprotegido - Um construtor que recebe um nome de destinatário e o armazena
- Um método público
send(const std::string& message)que imprime:Sending to <recipient>: <message>
- Um membro
EmailNotifier.h: Defina uma classeEmailNotifierque herda publicamente deNotifier:- Um construtor que recebe um destinatário e o passa para a classe base
- Sobrescreva o método
send()para primeiro chamar a versão da classe base usandoNotifier::send()e, em seguida, imprimir:[Email sent successfully]
main.cpp: Leia duas entradas (cada uma em uma linha separada):- Nome do destinatário (string)
- Conteúdo da mensagem (string)
Defina uma classe
SMSNotifierdiretamente em main.cpp que herda publicamente deNotifier:- Um construtor que recebe um destinatário e o passa para a classe base
- Sobrescreva o método
send()para imprimir:SMS to <recipient>: <message>(substituindo completamente o comportamento da classe base sem chamá-lo)
Crie um objeto de cada tipo de notificador (o
Notifierbase, oEmailNotifiere oSMSNotifier) usando o mesmo destinatário. Chamesend()em cada um com a mensagem de entrada, imprimindo uma linha separadora entre cada chamada:--- Base Notifier --- <output> --- Email Notifier --- <output> --- SMS Notifier --- <output>
Por exemplo, com as entradas Alice e Hello there!:
--- Base Notifier ---
Sending to Alice: Hello there!
--- Email Notifier ---
Sending to Alice: Hello there!
[Email sent successfully]
--- SMS Notifier ---
SMS to Alice: Hello there!Observe como EmailNotifier estende o comportamento da classe base chamando primeiro o método da classe pai, enquanto SMSNotifier o substitui completamente por sua própria implementação. Não se esqueça dos protetores de cabeçalho e de incluir os cabeçalhos apropriados em cada arquivo.
Experimente você mesmo
#include <iostream>
#include <string>
#include "Notifier.h"
#include "EmailNotifier.h"
using namespace std;
// TODO: Defina a classe SMSNotifier aqui que herda publicamente de Notifier
// - Construtor que passa recipient para a classe base
// - Sobrescreva send() para imprimir: SMS to <recipient>: <message>
// (substitua completamente o comportamento base, não chame o send() pai)
int main() {
string recipient;
string message;
getline(cin, recipient);
getline(cin, message);
// TODO: Crie um objeto Notifier com recipient
// Imprima "--- Base Notifier ---"
// Chame send() com message
// TODO: Crie um objeto EmailNotifier com recipient
// Imprima "--- Email Notifier ---"
// Chame send() com message
// TODO: Crie um objeto SMSNotifier com recipient
// Imprima "--- SMS Notifier ---"
// Chame send() com message
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos externosBuild e compilação em C++Arquivos de cabeçalho e arquivos-fonteNamespaces e escopoIntrodução à POO em C++Classes vs. objetosO ponteiro 'this'Métodos (funções membro)Atributos (membros de dados)Noções básicas de construtores e destruidoresRevisão - calculadora simples4Propriedades de Classe
Membros de Instância vs. EstáticosGetters e SettersFunções Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRevisão - Gerenciador de Contas Bancárias7Herança
Herança básicaNíveis de acesso da herançaOrdem de chamada de construtor e destrutorSobrescrita de métodosFunções virtuais e VTableHerança múltiplaHerança virtualRevisão - Hierarquia de funcionários10Visão geral da STL
Visão geral e filosofia da STLContêineres da STLIteradoresAlgoritmos da STLFunctors e expressões lambdaRecapitulação - Frequência de palavras13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonFactory e Abstract FactoryPadrão BuilderPadrão ObserverPadrão Strategy2Gerenciamento de Memória
Memória Stack vs. HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Ponteiros Inteligentes em C++RAII em C++Revisão - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de acesso em C++Especificadores de acesso em profundidadeOcultação de informaçõesStruct vs ClasseClasses aninhadas e internasRecapitulação - Sistema de registros de alunos8Polimorfismo
Polimorfismo em compilação vs. execuçãoSobrecarga de funçõesFunções virtuais revisadasFunções virtuais purasClasses abstratasDesign de interfaces em C++Conversão dinâmica e RTTIRecapitulação — Calculadora de formas11Conceitos Avançados de OOP
Composição vs. HerançaMixins via CRTPIdioma PimplApagamento de TiposClasses Enum e Tipagem ForteTratamento de Exceções em OOPHierarquias de Exceções Personalizadas14Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentoListas de Inicialização do ConstrutorConstrutores DelegantesAprofundamento em DestrutoresRegra dos Três / Cinco / ZeroRevisão - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de FluxoSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçõesTemplates de ClassesEspecialização de TemplatesTemplates VariádicosNoções Básicas de SFINAE e Traits de TiposRecapitulação - Contêiner GenéricoPratique por conta própria: Compilador de C++ online