Menu
Coddy logo textTech

Dynamic Casting 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 para um ponteiro de classe derivada. O C++ fornece RTTI (Runtime Type Information) e dynamic_cast para essas situações.

dynamic_cast converte com segurança ponteiros ou referências dentro de uma hierarquia de herança. Ao contrário do 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 um ponteiro válido

if (dog) {
    dog->bark();  // Seguro para chamar o método específico de Dog
}

Cat* cat = dynamic_cast<Cat*>(animal);  // Falha: retorna nullptr

Importante: dynamic_cast só funciona 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 o dynamic_cast seja útil, o 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 de tipo explícita.

challenge icon

Desafio

Fácil

Vamos construir um sistema de inspeção de veículos que utiliza dynamic_cast para identificar e interagir com segurança com diferentes tipos de veículos. Você criará uma hierarquia de veículos onde um inspetor precisa realizar verificações específicas de tipo que apenas certos veículos suportam.

Você organizará seu código em três arquivos:

  • Vehicle.h: Defina uma classe base Vehicle que representa qualquer veículo no sistema:
    • Um membro protegido std::string licensePlate
    • Um construtor que inicializa a placa de licença
    • 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 privado int seatCount
    • Um construtor que recebe a placa de licença e a contagem de assentos
    • Sobrescreva getDescription() para retornar "Car: <licensePlate>"
    • Um método inspectSeatbelts() que imprime: Inspecting <seatCount> seatbelts in <licensePlate>

    Truck:

    • Um membro privado double cargoCapacity (em toneladas)
    • Um construtor que recebe a placa de licença 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 privado bool hasSidecar
    • Um construtor que recebe a placa de licença 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> se não tiver
  • main.cpp: Leia três entradas (cada uma em uma linha separada):
    1. Placa de licença do carro
    2. Placa de licença do caminhão
    3. Placa de licença da motocicleta

    Crie um Car com 4 assentos, um Truck com 10.5 toneladas de capacidade 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 converter 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. Limpe seus 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 o dynamic_cast retorna um ponteiro válido apenas 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. Este é o poder do RTTI — determinar o tipo real em tempo de execução e agir de acordo.

Folha de consulta

O C++ fornece RTTI (Runtime Type Information) e dynamic_cast para determinar o tipo real de um objeto em tempo de execução e converter ponteiros com segurança dentro de uma hierarquia de herança.

O dynamic_cast 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; }
};

Animal* animal = new Dog();
Dog* dog = dynamic_cast<Dog*>(animal);  // Sucesso: retorna um ponteiro válido

if (dog) {
    dog->bark();  // Seguro para chamar método específico de Dog
}

Cat* cat = dynamic_cast<Cat*>(animal);  // Falha: retorna nullptr

Importante: o dynamic_cast só funciona com tipos polimórficos (classes com pelo menos uma função virtual).

O operador typeid consulta 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 útil, o uso frequente de dynamic_cast geralmente indica um problema de design. Prefira funções virtuais quando possível.

Experimente você mesmo

#include <iostream>
#include <string>
#include "Vehicle.h"
#include "Vehicles.h"

using namespace std;

int main() {
    // Ler as entradas
    string carPlate, truckPlate, motorcyclePlate;
    cin >> carPlate;
    cin >> truckPlate;
    cin >> motorcyclePlate;

    // TODO: Criar um Car com 4 assentos
    // TODO: Criar um Truck com 10.5 toneladas de capacidade
    // TODO: Criar uma Motorcycle com um sidecar (true)

    // TODO: Armazenar todos os três em um array de ponteiros Vehicle*

    // TODO: Percorrer o array e para cada veículo:
    //   1. Imprimir sua descrição usando getDescription()
    //   2. Usar dynamic_cast para tentar converter para cada tipo derivado
    //   3. Se a conversão para Car* for bem-sucedida, chamar inspectSeatbelts()
    //   4. Se a conversão para Truck* for bem-sucedida, chamar inspectCargo()
    //   5. Se a conversão para Motorcycle* for bem-sucedida, chamar inspectHelmetStorage()
    //   6. Imprimir uma linha em branco entre a inspeção de cada veículo

    // TODO: Limpar 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