Upcasting
Teil des Abschnitts Objektorientierte Programmierung der C-Journey von Coddy. Lektion 31 von 61.
Upcasting bezeichnet den Vorgang, einen spezifischen (abgeleiteten) Typ wie seinen allgemeineren (Basis-)Typ zu behandeln. Im Kompositionsmodell von C bedeutet dies, einen Zeiger auf eine Struktur, die einen Basistyp einbettet, an eine Funktion zu übergeben, die den Basistyp erwartet.
Betrachte eine Vehicle-Basisstruktur und ein Car, das sie enthält:
typedef struct {
int speed;
} Vehicle;
typedef struct {
Vehicle vehicle; // Basis als erstes Mitglied
int doors;
} Car;Stell dir nun eine generische Funktion vor, die mit jedem Fahrzeug funktioniert:
void accelerate(Vehicle* v, int amount) {
v->speed += amount;
}Dank der first member-Regel können wir einen Car* an diese Funktion übergeben, indem wir ihn in Vehicle* umwandeln:
Car my_car = {{0}, 4}; // speed=0, doors=4
accelerate((Vehicle*)&my_car, 50);
printf("Speed: %d\n", my_car.vehicle.speed); // Ausgabe: 50Das ist Upcasting: Wir bewegen uns von einem spezifischen Typ „nach oben“ zu einem allgemeineren. Die Funktion accelerate weiß nicht, dass sie mit einem Car arbeitet; sie sieht nur ein Vehicle. Dadurch kannst du wiederverwendbaren Code schreiben, der mit dem Basistyp arbeitet und dabei jeden abgeleiteten Typ akzeptiert, der ihn einbettet.
Aufgabe
EinfachErstellen wir ein einfaches Transportsystem, das Upcasting in Aktion demonstriert. Du erstellst einen Basistyp Vehicle und einen spezifischeren Typ Motorcycle und zeigst anschließend, wie eine generische Funktion durch Pointer-Casting mit beiden arbeiten kann.
Die Stärke des Upcastings besteht darin, dass du Funktionen für den Basistyp schreiben kannst und sie nahtlos mit jedem abgeleiteten Typ funktionieren, der ihn einbettet. Dein Motorrad „ist ein“ Fahrzeug, daher sollte jede Funktion, die einen Vehicle* erwartet, es verarbeiten können.
Du organisierst deinen Code über drei Dateien:
vehicle.h: Definiere deine Struct-Hierarchie mit Include-Guards. Erstelle eineVehicle-Struct mit zwei Feldern:speed(eine Ganzzahl für die aktuelle Geschwindigkeit) undmax_speed(eine Ganzzahl für die Höchstgeschwindigkeit des Fahrzeugs). Definiere anschließend eineMotorcycle-Struct, dieVehicleals ihr erstes Mitglied einbettet und einhas_sidecar-Feld hinzufügt (eine Ganzzahl, wobei 1 ja und 0 nein bedeutet). Deklariere zwei Funktionen:boost, die einenVehicle*und einen Betrag zur Erhöhung der Geschwindigkeit entgegennimmt, sowieprint_motorcycle, die einenMotorcycle*entgegennimmt.vehicle.c: Implementiere beide Funktionen. Deineboost-Funktion sollte die Geschwindigkeit des Fahrzeugs um den angegebenen Betrag erhöhen, sie aber beimax_speedbegrenzen. Wenn die neue Geschwindigkeit das Maximum überschreiten würde, setze sie stattdessen aufmax_speed. Deineprint_motorcycle-Funktion sollte alle informationen des Motorrads einschließlich der Daten des eingebetteten Fahrzeugs anzeigen.main.c: Erstelle einMotorcycleund initialisiere all seine Felder. Demonstriere anschließend Upcasting, indem du dieboost-Funktion aufrufst: Wandle deinenMotorcycle*in einenVehicle*um, um ihn an diese generische Funktion zu übergeben. Gib nach dem Erhöhen der Geschwindigkeit die informationen des Motorrads aus, um das Ergebnis zu sehen.
Du erhältst vier Eingaben: die Anfangsgeschwindigkeit des Motorrads, seine Höchstgeschwindigkeit, ob es einen Beiwagen hat (1 oder 0), und den Erhöhungsbetrag.
Deine Ausgabe sollte wie folgt aussehen:
Speed: 75
Max Speed: 120
Sidecar: YesWobei 75 die Geschwindigkeit nach der Erhöhung ist (anfänglich 50 + Erhöhung 25), 120 die Höchstgeschwindigkeit ist und Yes oder No angibt, ob das Motorrad einen Beiwagen hat. Wenn die Erhöhung die Geschwindigkeit über max_speed hinaus steigern würde, sollte die Geschwindigkeit auf max_speed begrenzt werden.
Die wichtigste Erkenntnis ist, dass boost nichts über Motorräder weiß. Es arbeitet nur mit Vehicle. Durch Upcasting kann dein Motorrad diese generische Funktion dennoch verwenden, um seine eingebetteten Fahrzeugdaten zu verändern.
Probier es selbst
#include <stdio.h>
#include "vehicle.h"
int main() {
int initial_speed, max_speed, has_sidecar, boost_amount;
// Eingaben lesen
scanf("%d", &initial_speed);
scanf("%d", &max_speed);
scanf("%d", &has_sidecar);
scanf("%d", &boost_amount);
// TODO: Erstelle ein Motorcycle und initialisiere alle seine Felder
// Denk daran: Motorcycle bettet Vehicle als sein erstes Mitglied ein
// TODO: Demonstriere Upcasting durch den Aufruf von boost
// Caste dein Motorcycle* zu Vehicle*, um es an die boost-Funktion zu übergeben
// TODO: Gib die Informationen des Motorcycle aus
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der modularen Programmierung
Header-DateienInclude GuardsQuelldateienStatische FunktionenWiederholung: Modularer Taschenrechner4Kapselung
Konzept der Opaque PointersOpaque Structs definierenGetter und SetterValidierung in SetternRückblick: Die geheime Box2Objekte und Methoden
Structs als ObjekteDer 'Self'-PointerConst-CorrectnessPointer vs. WertHilfsmethodenZusammenfassung: Point Manager5Projekt: Einfaches Bankkonto
Projekt-SetupImplementierung des Kontos3Objekt-Lebenszyklus
Konstruktor-MusterDestruktor-MusterStack-InitialisierungTiefe KopieRückblick: String-Wrapper6Vererbung durch Komposition
Struct-EinbettungDie First-Member-RegelZugriff auf Parent-MemberUpcastingRückblick: Formenhierarchie9Projekt: Formen-Zeichner
ProjektübersichtKreis-ImplementierungRechteck-ImplementierungPolymorphe VerwendungShape-ContainerÜbe selbstständig: Online-C-Compiler