Menu
Coddy logo textTech

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 nullptr

Importante: 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 Dog

Embora 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.

challenge icon

Desafio

Fácil

Vamos 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 base Vehicle que represente qualquer veículo no sistema:
    • Um membro std::string licensePlate protegido
    • Um construtor que inicializa a placa
    • Um método virtual getDescription() que retorna "Vehicle: <licensePlate>"
    • Um destrutor virtual
  • Vehicles.h: Defina três tipos de veículos derivados:

    Car:

    • Um membro int seatCount privado
    • 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 cargoCapacity privado (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 hasSidecar privado
    • 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, ou No helmet storage in <licensePlate> caso contrário
  • main.cpp: Leia três entradas (cada uma em uma linha separada):
    1. Placa do Car
    2. Placa do Truck
    3. Placa da Motorcycle

    Crie um Car com 4 assentos, um Truck com capacidade de 10.5 toneladas e uma Motorcycle com um sidecar. Armazene os três em um array de ponteiros Vehicle*.

    Percorra o array e, para cada veículo:

    1. Imprima sua descrição usando getDescription()
    2. Use dynamic_cast para tentar fazer a conversão para cada tipo derivado
    3. Se a conversão para Car* for bem-sucedida, chame inspectSeatbelts()
    4. Se a conversão para Truck* for bem-sucedida, chame inspectCargo()
    5. Se a conversão para Motorcycle* for bem-sucedida, chame inspectHelmetStorage()

    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-789

Observe 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;
}
quiz iconTeste seus conhecimentos

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

Pratique por conta própria: Compilador de C++ online