Conversão dinâmica e RTTI
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 62 de 104.
Às vezes, ao trabalhar com polimorfismo, você precisa determinar o tipo real de um objeto em tempo de execução ou converter com segurança um ponteiro de classe base em um ponteiro de classe derivada. O C++ fornece RTTI (informações de tipo em tempo de execução) e dynamic_cast para essas situações.
dynamic_cast converte com segurança ponteiros ou referências dentro de uma hierarquia de herança. Diferentemente de static_cast, ele realiza uma verificação em tempo de execução e retorna nullptr se a conversão for inválida:
class Animal {
public:
virtual ~Animal() = default;
};
class Dog : public Animal {
public:
void bark() { std::cout << "Woof!" << std::endl; }
};
class Cat : public Animal {};
Animal* animal = new Dog();
Dog* dog = dynamic_cast<Dog*>(animal); // Sucesso: retorna ponteiro válido
if (dog) {
dog->bark(); // Seguro chamar método específico de Dog
}
Cat* cat = dynamic_cast<Cat*>(animal); // Falha: retorna nullptrImportante: dynamic_cast funciona apenas com tipos polimórficos (classes com pelo menos uma função virtual). O operador typeid permite consultar o tipo real de um objeto:
#include <typeinfo>
Animal* pet = new Dog();
std::cout << typeid(*pet).name() << std::endl; // Exibe informações de tipo para DogEmbora dynamic_cast seja útil, seu uso frequente muitas vezes indica um problema de design. Prefira funções virtuais quando possível, pois elas permitem que o objeto lide com o comportamento específico do tipo sem verificação explícita de tipo.
Desafio
FácilVamos criar um sistema de inspeção de veículos que usa dynamic_cast para identificar e interagir com segurança com diferentes tipos de veículos. Você criará uma hierarquia de veículos na qual um inspetor precisa realizar verificações específicas de tipo que apenas determinados veículos oferecem.
Você organizará seu código em três arquivos:
Vehicle.h: Defina uma classe baseVehicleque represente qualquer veículo no sistema:- Um membro
std::string licensePlateprotegido - Um construtor que inicializa a placa
- Um método virtual
getDescription()que retorna"Vehicle: <licensePlate>" - Um destrutor virtual
- Um membro
Vehicles.h: Defina três tipos de veículos derivados:Car:- Um membro
int seatCountprivado - Um construtor que recebe a placa e a quantidade de assentos
- Sobrescreva
getDescription()para retornar"Car: <licensePlate>" - Um método
inspectSeatbelts()que imprime:Inspecting <seatCount> seatbelts in <licensePlate>
Truck:- Um membro
double cargoCapacityprivado (em toneladas) - Um construtor que recebe a placa e a capacidade de carga
- Sobrescreva
getDescription()para retornar"Truck: <licensePlate>" - Um método
inspectCargo()que imprime:Inspecting cargo area (<cargoCapacity> tons) in <licensePlate>
Motorcycle:- Um membro
bool hasSidecarprivado - Um construtor que recebe a placa e o status do sidecar
- Sobrescreva
getDescription()para retornar"Motorcycle: <licensePlate>" - Um método
inspectHelmetStorage()que imprime:Inspecting helmet storage in <licensePlate>se tiver um sidecar, ouNo helmet storage in <licensePlate>caso contrário
- Um membro
main.cpp: Leia três entradas (cada uma em uma linha separada):- Placa do Car
- Placa do Truck
- Placa da Motorcycle
Crie um
Carcom 4 assentos, umTruckcom capacidade de 10.5 toneladas e umaMotorcyclecom um sidecar. Armazene os três em um array de ponteirosVehicle*.Percorra o array e, para cada veículo:
- Imprima sua descrição usando
getDescription() - Use
dynamic_castpara tentar fazer a conversão para cada tipo derivado - Se a conversão para
Car*for bem-sucedida, chameinspectSeatbelts() - Se a conversão para
Truck*for bem-sucedida, chameinspectCargo() - Se a conversão para
Motorcycle*for bem-sucedida, chameinspectHelmetStorage()
Imprima uma linha em branco entre a inspeção de cada veículo. Libere os objetos alocados dinamicamente quando terminar.
Por exemplo, com as entradas ABC-123, TRK-456 e MTR-789:
Car: ABC-123
Inspecting 4 seatbelts in ABC-123
Truck: TRK-456
Inspecting cargo area (10.5 tons) in TRK-456
Motorcycle: MTR-789
Inspecting helmet storage in MTR-789Observe como dynamic_cast retorna um ponteiro válido somente quando o tipo real do objeto corresponde ao tipo de destino. Para cada veículo, apenas uma das três conversões será bem-sucedida, permitindo que você chame com segurança o método de inspeção específico do tipo. Esse é o poder de RTTI: determinar o tipo real em tempo de execução e agir de acordo.
Experimente você mesmo
#include <iostream>
#include <string>
#include "Vehicle.h"
#include "Vehicles.h"
using namespace std;
int main() {
// Ler entradas
string carPlate, truckPlate, motorcyclePlate;
cin >> carPlate;
cin >> truckPlate;
cin >> motorcyclePlate;
// TODO: Crie um Car com 4 assentos
// TODO: Crie um Truck com capacidade de 10.5 toneladas
// TODO: Crie uma Motorcycle com um sidecar (true)
// TODO: Armazene os três em um array de ponteiros Vehicle*
// TODO: Percorra o array e para cada veículo:
// 1. Imprima sua descrição usando getDescription()
// 2. Use dynamic_cast para tentar o casting para cada tipo derivado
// 3. Se o cast para Car* for bem-sucedido, chame inspectSeatbelts()
// 4. Se o cast para Truck* for bem-sucedido, chame inspectCargo()
// 5. If cast to Motorcycle* succeeds, call inspectHelmetStorage()
// 6. Imprima uma linha em branco entre a inspeção de cada veículo
// TODO: Limpe os objetos alocados dinamicamente
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