Upcasting
Teil des Abschnitts Objektorientierte Programmierung der C-Journey von Coddy. Lektion 31 von 61.
Upcasting bezeichnet den Vorgang, einen spezifischen (abgeleiteten) Typ als 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; // Basistyp als erstes Element
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 dieser Funktion ein Car* übergeben, indem wir es 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. So 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 zeigt. Du erstellst einen Basistyp Vehicle und einen spezifischeren Typ Motorcycle und zeigst dann, wie eine generische Funktion mit beiden über Zeiger-Casting arbeiten kann.
Der Vorteil von Upcasting ist, dass du Funktionen für den Basistyp schreiben kannst, die 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 auf drei Dateien:
vehicle.h: Definiere deine Struct-Hierarchie mit Include-Guards. Erstelle einVehicle-Struct mit zwei Feldern:speed(eine Ganzzahl für die aktuelle Geschwindigkeit) undmax_speed(eine Ganzzahl für die Höchstgeschwindigkeit des Fahrzeugs). Definiere dann einMotorcycle-Struct, dasVehicleals erstes Element einbettet und ein Feldhas_sidecarhinzufü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, undprint_motorcycle, die einenMotorcycle*entgegennimmt.vehicle.c: Implementiere beide Funktionen. Deine Funktionboostsoll die Geschwindigkeit des Fahrzeugs um den angegebenen Betrag erhöhen, sie jedoch aufmax_speedbegrenzen. Würde die neue Geschwindigkeit das Maximum überschreiten, setze sie stattdessen aufmax_speed. Deine Funktionprint_motorcyclesoll alle Informationen zum Motorrad anzeigen, einschließlich der eingebetteten Fahrzeugdaten.main.c: Erstelle einMotorcycleund initialisiere alle seine Felder. Zeige dann Upcasting, indem du die Funktionboostaufrufst: Wandle deinenMotorcycle*in einenVehicle*um, um ihn an diese generische Funktion zu übergeben. Gib nach der Erhöhung die Informationen zum Motorrad 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 so aussehen:
Speed: 75
Max Speed: 120
Sidecar: YesDabei ist 75 die Geschwindigkeit nach der Erhöhung (Anfangsgeschwindigkeit 50 + Erhöhung 25), 120 die Höchstgeschwindigkeit, und Yes oder No gibt an, 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 hier ist, dass boost nichts über Motorräder weiß. Die Funktion arbeitet nur mit Vehicle. Durch Upcasting kann dein Motorrad dennoch diese generische Funktion nutzen, um seine eingebetteten Fahrzeugdaten zu ändern.
Probier es selbst
#include <stdio.h>
#include "vehicle.h"
int main() {
int initial_speed, max_speed, has_sidecar, boost_amount;
// Eingaben einlesen
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
// Denke daran: Motorcycle enthält Vehicle als erstes Mitglied
// TODO: Demonstriere Upcasting durch einen Aufruf von boost
// Wandle deinen Motorcycle* in Vehicle* um, um ihn an die Funktion boost zu übergeben
// TODO: Gib die Informationen des Motorrads 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