Menu
Coddy logo textTech

Upcasting

Parte de la sección Object Oriented Programming del Journey de C de Coddy — lección 31 de 61.

Upcasting es el acto de tratar un tipo específico (derivado) como su tipo más general (base). En el modelo de composición de C, esto significa pasar un puntero a un struct que incorpora un tipo base a una función que espera el tipo base.

Considera una estructura base Vehicle y un Car que la contiene:

typedef struct {
    int speed;
} Vehicle;

typedef struct {
    Vehicle vehicle;  // Base como primer miembro
    int doors;
} Car;

Ahora imagina una función genérica que funcione con cualquier vehículo:

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

Gracias a la regla del primer miembro, podemos pasar un Car* a esta función realizando un casting a Vehicle*:

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

Esto es upcasting: nos estamos moviendo "hacia arriba" desde un tipo específico a uno general. La función accelerate no sabe que está trabajando con un Car; solo ve un Vehicle. Esto le permite escribir código reutilizable que opera sobre el tipo base mientras acepta cualquier tipo derivado que lo incorpore.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de transporte simple que demuestre el upcasting en acción. Crearás un tipo base Vehicle y un tipo más específico Motorcycle, para luego mostrar cómo una función genérica puede trabajar con ambos a través del casting de punteros.

El poder del upcasting es que puedes escribir funciones para el tipo base y estas funcionarán sin problemas con cualquier tipo derivado que lo contenga. Tu motocicleta "es un" vehículo, por lo que cualquier función que espere un Vehicle* debería poder procesarla.

Organizarás tu código en tres archivos:

  • vehicle.h: Define tu jerarquía de estructuras con guardas de inclusión. Crea una estructura Vehicle con dos campos: speed (un entero para la velocidad actual) y max_speed (un entero para la velocidad máxima del vehículo). Luego define una estructura Motorcycle que incluya a Vehicle como su primer miembro y añada un campo has_sidecar (un entero, donde 1 significa sí y 0 significa no). Declara dos funciones: boost que recibe un Vehicle* y una cantidad para aumentar la velocidad, y print_motorcycle que recibe un Motorcycle*.
  • vehicle.c: Implementa ambas funciones. Tu función boost debe aumentar la velocidad del vehículo en la cantidad dada, pero limitándola a max_speed; si la nueva velocidad excediera el máximo, establécela en max_speed en su lugar. Tu función print_motorcycle debe mostrar toda la información de la motocicleta, incluyendo los datos del vehículo integrados.
  • main.c: Crea una Motorcycle e inicializa todos sus campos. Luego demuestra el upcasting llamando a la función boost: realiza un cast de tu Motorcycle* a Vehicle* para pasarlo a esta función genérica. Después de aplicar el boost, imprime la información de la motocicleta para ver el resultado.

Recibirás cuatro entradas: la velocidad inicial de la motocicleta, su velocidad máxima, si tiene sidecar (1 o 0) y la cantidad de boost.

Tu salida debería verse así:

Speed: 75
Max Speed: 120
Sidecar: Yes

Donde 75 es la velocidad después del boost (inicial 50 + boost 25), 120 es la velocidad máxima, y Yes o No indica si la motocicleta tiene sidecar. Si el boost empujara la velocidad más allá de max_speed, la velocidad debe limitarse a max_speed.

El aprendizaje clave aquí es que boost no sabe nada sobre motocicletas; solo trabaja con Vehicle. Sin embargo, a través del upcasting, tu motocicleta puede usar esta función genérica para modificar sus datos de vehículo integrados.

Hoja de referencia

El upcasting trata un tipo específico (derivado) como su tipo más general (base) pasando un puntero a una estructura que contiene un tipo base a una función que espera el tipo base.

Define una estructura base y una estructura derivada que la incluya como el primer miembro:

typedef struct {
    int speed;
} Vehicle;

typedef struct {
    Vehicle vehicle;  // Base como primer miembro
    int doors;
} Car;

Crea una función que trabaje con el tipo base:

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

Realiza un casting de un puntero de tipo derivado al puntero de tipo base para llamar a la función:

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

Esto te permite escribir código reutilizable que opera sobre el tipo base mientras acepta cualquier tipo derivado que lo incluya.

Pruébalo tú mismo

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

int main() {
    int initial_speed, max_speed, has_sidecar, boost_amount;
    
    // Leer entradas
    scanf("%d", &initial_speed);
    scanf("%d", &max_speed);
    scanf("%d", &has_sidecar);
    scanf("%d", &boost_amount);
    
    // TODO: Crear una Motorcycle e inicializar todos sus campos
    // Recuerda: Motorcycle incorpora Vehicle como su primer miembro
    
    // TODO: Demostrar el upcasting llamando a boost
    // Convierte tu Motorcycle* a Vehicle* para pasarlo a la función boost
    
    // TODO: Imprimir la información de la motocicleta
    
    return 0;
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Object Oriented Programming