Menu
Coddy logo textTech

Upcasting

Parte da seção Object Oriented Programming do Journey de C da Coddy — lição 31 de 61.

Upcasting é o ato de tratar um tipo específico (derivado) como seu tipo mais geral (base). No modelo de composição de C, isso significa passar um ponteiro para uma struct que incorpora um tipo base para uma função que espera o tipo base.

Considere uma struct base Vehicle e um Car que a contém:

typedef struct {
    int speed;
} Vehicle;

typedef struct {
    Vehicle vehicle;  // Base como primeiro membro
    int doors;
} Car;

Agora imagine uma função genérica que funciona com qualquer veículo:

void accelerate(Vehicle* v, int amount) {
    v->speed += amount;
}

Graças à regra do primeiro membro, podemos passar um Car* para esta função fazendo o casting para Vehicle*:

Car my_car = {{0}, 4};  // velocidade=0, portas=4
accelerate((Vehicle*)&my_car, 50);
printf("Speed: %d\n", my_car.vehicle.speed);  // Saída: 50

Isso é upcasting — estamos nos movendo "para cima", de um tipo específico para um geral. A função accelerate não sabe que está trabalhando com um Car; ela vê apenas um Vehicle. Isso permite que você escreva código reutilizável que opera no tipo base enquanto aceita qualquer tipo derivado que o incorpore.

challenge icon

Desafio

Fácil

Vamos construir um sistema de transporte simples que demonstra o upcasting em ação. Você criará um tipo base Vehicle e um tipo mais específico Motorcycle, e então mostrará como uma função genérica pode funcionar com ambos através de conversão de ponteiro (pointer casting).

O poder do upcasting é que você pode escrever funções para o tipo base, e elas funcionarão perfeitamente com qualquer tipo derivado que o incorpore. Sua motocicleta "é um" veículo, então qualquer função que espere um Vehicle* deve ser capaz de processá-la.

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

  • vehicle.h: Defina sua hierarquia de structs com guards de inclusão. Crie uma struct Vehicle com dois campos: speed (um inteiro para a velocidade atual) e max_speed (um inteiro para a velocidade máxima do veículo). Em seguida, defina uma struct Motorcycle que incorpora Vehicle como seu primeiro membro e adiciona um campo has_sidecar (um inteiro, onde 1 significa sim e 0 significa não). Declare duas funções: boost que recebe um Vehicle* e um valor para aumentar a velocidade, e print_motorcycle que recebe um Motorcycle*.
  • vehicle.c: Implemente ambas as funções. Sua função boost deve aumentar a velocidade do veículo pelo valor fornecido, mas limitá-la ao max_speed—se a nova velocidade exceder o máximo, defina-a como max_speed. Sua função print_motorcycle deve exibir todas as informações da motocicleta, incluindo os dados do veículo incorporado.
  • main.c: Crie uma Motorcycle e inicialize todos os seus campos. Em seguida, demonstre o upcasting chamando a função boost—converta seu Motorcycle* para um Vehicle* para passá-lo a esta função genérica. Após o boost, imprima as informações da motocicleta para ver o resultado.

Você receberá quatro entradas: a velocidade inicial da motocicleta, sua velocidade máxima, se ela tem um sidecar (1 ou 0) e o valor do boost.

Sua saída deve ser parecida com esta:

Speed: 75
Max Speed: 120
Sidecar: Yes

Onde 75 é a velocidade após o boost (inicial 50 + boost 25), 120 é a velocidade máxima, e Yes ou No indica se a motocicleta tem um sidecar. Se o boost empurrar a velocidade além do max_speed, a velocidade deve ser limitada ao max_speed.

O aprendizado principal aqui é que a função boost não sabe nada sobre motocicletas—ela trabalha apenas com Vehicle. No entanto, através do upcasting, sua motocicleta pode usar essa função genérica para modificar seus dados de veículo incorporados.

Folha de consulta

O upcasting trata um tipo específico (derivado) como seu tipo mais geral (base), passando um ponteiro para uma struct que incorpora um tipo base para uma função que espera o tipo base.

Defina uma struct base e uma struct derivada que a incorpora como o primeiro membro:

typedef struct {
    int speed;
} Vehicle;

typedef struct {
    Vehicle vehicle;  // Base as first member
    int doors;
} Car;

Crie uma função que funcione com o tipo base:

void accelerate(Vehicle* v, int amount) {
    v->speed += amount;
}

Converta um ponteiro de tipo derivado para o ponteiro de tipo base para chamar a função:

Car my_car = {{0}, 4};  // speed=0, doors=4
accelerate((Vehicle*)&my_car, 50);
printf("Speed: %d\n", my_car.vehicle.speed);  // Output: 50

Isso permite que você escreva código reutilizável que opera no tipo base enquanto aceita qualquer tipo derivado que o incorpore.

Experimente você mesmo

#include <stdio.h>
#include "vehicle.h"

int main() {
    int initial_speed, max_speed, has_sidecar, boost_amount;
    
    // Ler as entradas
    scanf("%d", &initial_speed);
    scanf("%d", &max_speed);
    scanf("%d", &has_sidecar);
    scanf("%d", &boost_amount);
    
    // TODO: Criar uma Motorcycle e inicializar todos os seus campos
    // Lembre-se: Motorcycle incorpora Vehicle como seu primeiro membro
    
    // TODO: Demonstrar upcasting chamando boost
    // Converta seu Motorcycle* para Vehicle* para passá-lo para a função boost
    
    // TODO: Imprimir as informações da motocicleta
    
    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 Object Oriented Programming