Upcasting
Fait partie de la section Object Oriented Programming du Journey C de Coddy — leçon 31 sur 61.
L'upcasting est l'action de traiter un type spécifique (dérivé) comme son type plus général (de base). Dans le modèle de composition du C, cela signifie passer un pointeur vers une struct qui incorpore un type de base à une fonction attendant le type de base.
Considérez une structure de base Vehicle et une Car qui la contient :
typedef struct {
int speed;
} Vehicle;
typedef struct {
Vehicle vehicle; // La base comme premier membre
int doors;
} Car;Imaginez maintenant une fonction générique qui fonctionne avec n'importe quel véhicule :
void accelerate(Vehicle* v, int amount) {
v->speed += amount;
}Grâce à la règle du premier membre, nous pouvons passer un Car* à cette fonction en le transtypant en Vehicle* :
Car my_car = {{0}, 4}; // vitesse=0, portes=4
accelerate((Vehicle*)&my_car, 50);
printf("Speed: %d\n", my_car.vehicle.speed); // Sortie : 50C'est ce qu'on appelle l'upcasting (transtypage ascendant) — nous passons d'un type spécifique à un type plus général. La fonction accelerate ne sait pas qu'elle travaille avec une Car ; elle ne voit qu'un Vehicle. Cela vous permet d'écrire du code réutilisable qui opère sur le type de base tout en acceptant n'importe quel type dérivé qui l'incorpore.
Défi
FacileConstruisons un système de transport simple qui démontre l'upcasting en action. Vous allez créer un type de base Vehicle et un type plus spécifique Motorcycle, puis montrer comment une fonction générique peut fonctionner avec les deux grâce au transtypage de pointeurs (pointer casting).
La puissance de l'upcasting réside dans le fait que vous pouvez écrire des fonctions pour le type de base, et qu'elles fonctionneront de manière transparente avec n'importe quel type dérivé qui l'incorpore. Votre moto "est un" véhicule, donc toute fonction attendant un Vehicle* devrait pouvoir la traiter.
Vous organiserez votre code sur trois fichiers :
vehicle.h: Définissez votre hiérarchie de structures avec des gardes d'inclusion. Créez une structureVehicleavec deux champs :speed(un entier pour la vitesse actuelle) etmax_speed(un entier pour la vitesse maximale du véhicule). Définissez ensuite une structureMotorcyclequi incorporeVehiclecomme premier membre et ajoute un champhas_sidecar(un entier, où 1 signifie oui et 0 signifie non). Déclarez deux fonctions :boostqui prend unVehicle*et un montant pour augmenter la vitesse, etprint_motorcyclequi prend unMotorcycle*.vehicle.c: Implémentez les deux fonctions. Votre fonctionboostdoit augmenter la vitesse du véhicule du montant donné, mais la plafonner àmax_speed— si la nouvelle vitesse dépasse le maximum, fixez-la àmax_speedà la place. Votre fonctionprint_motorcycledoit afficher toutes les informations de la moto, y compris les données du véhicule incorporé.main.c: Créez uneMotorcycleet initialisez tous ses champs. Démontrez ensuite l'upcasting en appelant la fonctionboost— transtypez votreMotorcycle*en unVehicle*pour le passer à cette fonction générique. Après le boost, affichez les informations de la moto pour voir le résultat.
Vous recevrez quatre entrées : la vitesse initiale de la moto, sa vitesse maximale, si elle a un side-car (1 ou 0), et le montant du boost.
Votre sortie devrait ressembler à ceci :
Speed: 75
Max Speed: 120
Sidecar: YesOù 75 est la vitesse après le boost (initiale 50 + boost 25), 120 est la vitesse maximale, et Yes ou No indique si la moto a un side-car. Si le boost pousse la vitesse au-delà de max_speed, la vitesse doit être plafonnée à max_speed.
L'apprentissage clé ici est que boost ne connaît rien aux motos — elle ne travaille qu'avec Vehicle. Pourtant, grâce à l'upcasting, votre moto peut utiliser cette fonction générique pour modifier ses données de véhicule incorporées.
Aide-mémoire
Le transtypage ascendant (upcasting) traite un type spécifique (dérivé) comme son type plus général (de base) en passant un pointeur vers une structure qui intègre un type de base à une fonction attendant le type de base.
Définissez une structure de base et une structure dérivée qui l'intègre comme premier membre :
typedef struct {
int speed;
} Vehicle;
typedef struct {
Vehicle vehicle; // La base comme premier membre
int doors;
} Car;Créez une fonction qui travaille avec le type de base :
void accelerate(Vehicle* v, int amount) {
v->speed += amount;
}Transtypez un pointeur de type dérivé vers le pointeur de type de base pour appeler la fonction :
Car my_car = {{0}, 4}; // speed=0, doors=4
accelerate((Vehicle*)&my_car, 50);
printf("Speed: %d\n", my_car.vehicle.speed); // Sortie : 50Cela vous permet d'écrire du code réutilisable qui opère sur le type de base tout en acceptant n'importe quel type dérivé qui l'intègre.
Essayez vous-même
#include <stdio.h>
#include "vehicle.h"
int main() {
int initial_speed, max_speed, has_sidecar, boost_amount;
// Lire les entrées
scanf("%d", &initial_speed);
scanf("%d", &max_speed);
scanf("%d", &has_sidecar);
scanf("%d", &boost_amount);
// TODO: Créer une Motorcycle et initialiser tous ses champs
// Rappel : Motorcycle intègre Vehicle comme son premier membre
// TODO: Démontrer le surclassement (upcasting) en appelant boost
// Convertissez votre Motorcycle* en Vehicle* pour le passer à la fonction boost
// TODO: Afficher les informations de la moto
return 0;
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Object Oriented Programming
1Bases de la programmation modulaire
Fichiers d'en-têteGardes d'inclusionFichiers sourcesFonctions statiquesRécapitulatif : Calculatrice modulaire4Encapsulation
Concept des pointeurs opaquesDéfinir des structures opaquesGetters et settersValidation dans les settersRécapitulatif : La boîte secrète2Objets et méthodes
Structs comme objetsLe pointeur 'Self'Rigueur du mot-clé constPointeur vs ValeurMéthodes utilitairesRécapitulatif : Point Manager5Projet : Compte bancaire simple
Configuration du projetImplémentation du compte8Polymorphisme
Pointeurs de fonctions dans les structuresSimulation de méthodesLe concept d'interfaceImplémentation d'interfacesItération polymorphiqueRécapitulatif : Greeter11Patrons de conception en C
Patron SingletonPatron FabriquePatron ItérateurRécapitulatif : Logger Factory3Cycle de vie des objets
Pattern de constructeurPattern de destructeurInitialisation sur la pileCopie profondeRécapitulatif : String Wrapper6Héritage par composition
Imbrication de structLa règle du premier membreAccès aux membres parentsUpcastingRécapitulatif : Hiérarchie des formes9Projet : Dessinateur de formes
Aperçu du projetImplémentation du cercle