Upcasting
Parte de la sección Programación Orientada a Objetos del Journey de C de Coddy. Lección 31 de 61.
La conversión ascendente 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 una estructura 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 funciona 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 convirtiéndolo a Vehicle*:
Car my_car = {{0}, 4}; // speed=0, doors=4
accelerate((Vehicle*)&my_car, 50);
printf("Speed: %d\n", my_car.vehicle.speed); // Salida: 50Esto es un upcasting: estamos subiendo 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 te permite escribir código reutilizable que opera sobre el tipo base y acepta cualquier tipo derivado que lo incorpore.
Desafío
FácilConstruyamos un sistema de transporte sencillo que demuestre el upcasting en acción. Crearás un tipo base Vehicle y un tipo más específico Motorcycle, después mostrarás cómo una función genérica puede trabajar con ambos mediante la conversión de punteros.
El poder del upcasting es que puedes escribir funciones para el tipo base, y funcionarán sin problemas con cualquier tipo derivado que lo incluya. Tu Motorcycle "is a" vehículo, así que cualquier función que espere un Vehicle* debería poder procesarlo.
Organizarás tu código en tres archivos:
vehicle.h: Define tu jerarquía de structs con include guards. Create un structVehiclecon dos fields:speed(un entero para la velocidad current) ymax_speed(un entero para la velocidad máxima del vehículo). Después define un structMotorcycleque incluyeVehiclecomo su primer member y añade un campohas_sidecar(un entero, donde 1 significa sí y 0 significa no). Declara dos funciones:boost, que recibe unVehicle*y una cantidad para Increase la velocidad, yprint_motorcycle, que recibe unMotorcycle*.vehicle.c: Implement ambas funciones. Tu funciónboostdebe Increase la velocidad del vehículo en la cantidad given, pero limitarla amax_speed; si la nueva velocidad fuera a exceed el máximo, establécela enmax_speeden su lugar. Tu funciónprint_motorcycledebe mostrar all la información de la Motorcycle, incluida su información del vehículo incluido.main.c: Crea unaMotorcyclee initialize todos its campos. Después demuestra el upcasting llamando a la funciónboost: convierte tuMotorcycle*en unVehicle*para pasarlo a esta función genérica. Después del aumento, imprime la información de la Motorcycle para ver el resultado.
Recibirás cuatro entradas: la initial speed de la Motorcycle, su max speed, si has a sidecar (1 o 0), y la boost amount.
Tu salida debería verse así:
Speed: 75
Max Speed: 120
Sidecar: YesDonde 75 es la velocidad después del aumento (initial 50 + boost 25), 120 es la velocidad máxima, y Yes o No indica si la Motorcycle tiene un sidecar. Si el aumento hiciera que la velocidad superara max_speed, la velocidad debería limitarse a max_speed.
El aprendizaje clave aquí es que boost no sabe nada sobre Motorcycle. Solo trabaja con Vehicle. Sin embargo, mediante el upcasting, tu Motorcycle puede usar esta función genérica para modificar sus datos del vehículo incluido.
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: Crea una Motorcycle e inicializa todos sus campos
// Recuerda: Motorcycle embebe Vehicle como su primer miembro
// TODO: Demuestra el upcasting llamando a boost
// Convierte tu Motorcycle* a Vehicle* para pasarlo a la función boost
// TODO: Imprime la información de la motorcycle
return 0;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
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írculoImplementación del rectánguloUso polimórficoContenedor de figurasPractica por tu cuenta: Compilador de C online