Menu
Coddy logo textTech

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 : 50

C'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.

challenge icon

Défi

Facile

Construisons 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 structure Vehicle avec deux champs : speed (un entier pour la vitesse actuelle) et max_speed (un entier pour la vitesse maximale du véhicule). Définissez ensuite une structure Motorcycle qui incorpore Vehicle comme premier membre et ajoute un champ has_sidecar (un entier, où 1 signifie oui et 0 signifie non). Déclarez deux fonctions : boost qui prend un Vehicle* et un montant pour augmenter la vitesse, et print_motorcycle qui prend un Motorcycle*.
  • vehicle.c : Implémentez les deux fonctions. Votre fonction boost doit 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 fonction print_motorcycle doit afficher toutes les informations de la moto, y compris les données du véhicule incorporé.
  • main.c : Créez une Motorcycle et initialisez tous ses champs. Démontrez ensuite l'upcasting en appelant la fonction boost — transtypez votre Motorcycle* en un Vehicle* 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: Yes

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 : 50

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'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;
}
quiz iconTestez-vous

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