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: 50Esto 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.
Desafío
FácilVamos 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 estructuraVehiclecon dos campos:speed(un entero para la velocidad actual) ymax_speed(un entero para la velocidad máxima del vehículo). Luego define una estructuraMotorcycleque incluya aVehiclecomo su primer miembro y añada un campohas_sidecar(un entero, donde 1 significa sí y 0 significa no). Declara dos funciones:boostque recibe unVehicle*y una cantidad para aumentar la velocidad, yprint_motorcycleque recibe unMotorcycle*.vehicle.c: Implementa ambas funciones. Tu funciónboostdebe aumentar la velocidad del vehículo en la cantidad dada, pero limitándola amax_speed; si la nueva velocidad excediera el máximo, establécela enmax_speeden su lugar. Tu funciónprint_motorcycledebe mostrar toda la información de la motocicleta, incluyendo los datos del vehículo integrados.main.c: Crea unaMotorcyclee inicializa todos sus campos. Luego demuestra el upcasting llamando a la funciónboost: realiza un cast de tuMotorcycle*aVehicle*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: YesDonde 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: 50Esto 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;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Object Oriented Programming
1Fundamentos de programación modular
Archivos de cabeceraGuardas de inclusiónArchivos fuenteFunciones estáticasRepaso: Calculadora modular4Encapsulamiento
Concepto de punteros opacosDefinición de structs opacosGetters y SettersValidación en settersResumen: Caja secreta7Punteros a funciones
Declaración de punteros a funcionesLlamada a punteros a funcionesTypedef para punteros a funcionesPasar funciones como argumentosRepaso: Dispatch de calculadora10Contenedores genéricos
Repaso de punteros voidWrapper genéricoSwap genéricoComparación genéricaRepaso: Array genérico2Objetos y métodos
Structs como objetosEl puntero 'Self'Const CorrectnessPuntero vs. ValorMétodos auxiliaresResumen: Point Manager5Proyecto: Cuenta bancaria simple
Configuración del proyectoImplementación de la cuenta3Ciclo de vida de los objetos
Patrón ConstructorPatrón DestructorInicialización en StackCopia profundaResumen: String Wrapper6Herencia mediante composición
Incrustación de StructsLa regla del primer miembroAcceso a miembros padreUpcastingResumen: Jerarquía de formas9Proyecto: Dibujador de figuras
Descripción general del proyectoImplementación del círculo