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 consiste en 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 incluye un tipo base a una función que espera el tipo base.
Considera una estructura base Vehicle y una 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 una conversión ascendente: estamos pasando de 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 incluya.
Desafío
FácilConstruyamos un sistema de transporte sencillo que demuestre cómo funciona la conversión ascendente. Crearás un tipo base Vehicle y un tipo más específico Motorcycle, y luego mostrarás cómo una función genérica puede trabajar con ambos mediante la conversión de punteros.
La ventaja de la conversión ascendente es que puedes escribir funciones para el tipo base, y funcionarán sin problemas con cualquier tipo derivado que lo incluya. Tu motocicleta «es un» vehículo, así que cualquier función que espere un Vehicle* debería poder procesarla.
Organizarás tu código en tres archivos:
vehicle.h: Define la jerarquía de estructuras con protecciones 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 incluyaVehiclecomo su primer miembro y añada un campohas_sidecar(un entero, donde 1 significa que sí y 0 que no). Declara dos funciones:boost, que recibe unVehicle*y una cantidad para aumentar la velocidad, yprint_motorcycle, que recibe unMotorcycle*.vehicle.c: Implementa ambas funciones. La funciónboostdebe aumentar la velocidad del vehículo en la cantidad indicada, pero limitarla amax_speed; si la nueva velocidad superara el máximo, asígnalemax_speed. La funciónprint_motorcycledebe mostrar toda la información de la motocicleta, incluidos los datos de su vehículo integrado.main.c: Crea unaMotorcyclee inicializa todos sus campos. Luego demuestra la conversión ascendente llamando a la funciónboost: convierte tuMotorcycle*en unVehicle*para pasarlo a esta función genérica. Después de aumentar la velocidad, imprime la información de la motocicleta para ver el resultado.
Recibirás cuatro datos de entrada: la velocidad inicial de la motocicleta, su velocidad máxima, si tiene un sidecar (1 o 0) y la cantidad del aumento.
La salida debería verse así:
Speed: 75
Max Speed: 120
Sidecar: YesDonde 75 es la velocidad después del aumento (inicial 50 + aumento 25), 120 es la velocidad máxima y Yes o No indica si la motocicleta tiene un sidecar. Si el aumento hiciera que la velocidad superara max_speed, esta debería limitarse a max_speed.
La clave del aprendizaje aquí es que boost no sabe nada sobre motocicletas. Solo trabaja con Vehicle. Sin embargo, mediante la conversión ascendente, tu motocicleta puede usar esta función genérica para modificar los datos de su vehículo integrado.
Pruébalo tú mismo
#include <stdio.h>
#include "vehicle.h"
int main() {
int initial_speed, max_speed, has_sidecar, boost_amount;
// Lee las 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 contiene Vehicle como su primer miembro
// TODO: Demuestra la conversión a un tipo base llamando a boost
// Convierte tu Motorcycle* a Vehicle* para pasarlo a la función boost
// TODO: Muestra 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 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