Menu
Coddy logo textTech

Upcasting

Teil des Abschnitts Object Oriented Programming der C-Journey von Coddy — Lektion 31 von 61.

Upcasting ist der Vorgang, einen spezifischen (abgeleiteten) Typ als seinen allgemeineren (Basis-)Typ zu behandeln. Im Kompositionsmodell von C bedeutet dies, einen Zeiger auf ein struct, das einen Basistyp einbettet, an eine Funktion zu übergeben, die den Basistyp erwartet.

Betrachten Sie eine Vehicle-Basis-Struktur und ein Car, das diese enthält:

typedef struct {
    int speed;
} Vehicle;

typedef struct {
    Vehicle vehicle;  // Basis als erstes Element
    int doors;
} Car;

Stellen Sie sich 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 ein Car* an diese Funktion übergeben, indem wir es in Vehicle* umwandeln:

Car my_car = {{0}, 4};  // Geschwindigkeit=0, Türen=4
accelerate((Vehicle*)&my_car, 50);
printf("Speed: %d\n", my_car.vehicle.speed);  // Ausgabe: 50

Das ist Upcasting – wir bewegen uns von einem spezifischen Typ „nach oben“ zu einem allgemeinen Typ. Die Funktion accelerate weiß nicht, dass sie mit einem Car arbeitet; sie sieht nur ein Vehicle. Dies ermöglicht es Ihnen, wiederverwendbaren Code zu schreiben, der auf dem Basistyp operiert, während er jeden abgeleiteten Typ akzeptiert, der diesen einbettet.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein einfaches Transportsystem bauen, das Upcasting in Aktion demonstriert. Sie erstellen einen Basis-Typ Vehicle und einen spezifischeren Typ Motorcycle und zeigen dann, wie eine generische Funktion mit beiden durch Pointer-Casting arbeiten kann.

Die Stärke des Upcastings liegt darin, dass Sie Funktionen für den Basis-Typ schreiben können und diese nahtlos mit jedem abgeleiteten Typ funktionieren, der diesen einbettet. Ihr Motorrad "ist ein" Fahrzeug, daher sollte jede Funktion, die ein Vehicle* erwartet, in der Lage sein, es zu verarbeiten.

Sie organisieren Ihren Code in drei Dateien:

  • vehicle.h: Definieren Sie Ihre Struct-Hierarchie mit Include-Guards. Erstellen Sie ein Vehicle-Struct mit zwei Feldern: speed (eine Ganzzahl für die aktuelle Geschwindigkeit) und max_speed (eine Ganzzahl für die Höchstgeschwindigkeit des Fahrzeugs). Definieren Sie dann ein Motorcycle-Struct, das Vehicle als sein erstes Mitglied einbettet und ein Feld has_sidecar hinzufügt (eine Ganzzahl, wobei 1 "ja" und 0 "nein" bedeutet). Deklarieren Sie zwei Funktionen: boost, die einen Vehicle* und einen Betrag zur Geschwindigkeitserhöhung entgegennimmt, und print_motorcycle, die einen Motorcycle* entgegennimmt.
  • vehicle.c: Implementieren Sie beide Funktionen. Ihre boost-Funktion sollte die Geschwindigkeit des Fahrzeugs um den angegebenen Betrag erhöhen, sie jedoch bei max_speed deckeln – wenn die neue Geschwindigkeit das Maximum überschreiten würde, setzen Sie sie stattdessen auf max_speed. Ihre print_motorcycle-Funktion sollte alle Informationen des Motorrads einschließlich seiner eingebetteten Fahrzeugdaten anzeigen.
  • main.c: Erstellen Sie ein Motorcycle und initialisieren Sie alle seine Felder. Demonstrieren Sie dann Upcasting, indem Sie die boost-Funktion aufrufen – casten Sie Ihren Motorcycle* zu einem Vehicle*, um ihn an diese generische Funktion zu übergeben. Drucken Sie nach dem Boost die Informationen des Motorrads aus, um das Ergebnis zu sehen.

Sie erhalten vier Eingaben: die Anfangsgeschwindigkeit des Motorrads, seine Höchstgeschwindigkeit, ob es einen Beiwagen hat (1 oder 0) und den Boost-Betrag.

Ihre Ausgabe sollte wie folgt aussehen:

Speed: 75
Max Speed: 120
Sidecar: Yes

Wobei 75 die Geschwindigkeit nach dem Boost ist (Anfangswert 50 + Boost 25), 120 die Höchstgeschwindigkeit ist und Yes oder No angibt, ob das Motorrad einen Beiwagen hat. Wenn der Boost die Geschwindigkeit über max_speed treiben würde, sollte die Geschwindigkeit bei max_speed gedeckelt werden.

Die wichtigste Erkenntnis hierbei ist, dass boost nichts über Motorräder weiß – sie arbeitet nur mit Vehicle. Dennoch kann Ihr Motorrad durch Upcasting diese generische Funktion nutzen, um seine eingebetteten Fahrzeugdaten zu ändern.

Spickzettel

Upcasting behandelt einen spezifischen (abgeleiteten) Typ als seinen allgemeineren (Basis-)Typ, indem ein Zeiger auf ein Struct, das einen Basistyp einbettet, an eine Funktion übergeben wird, die den Basistyp erwartet.

Definieren Sie ein Basis-Struct und ein abgeleitetes Struct, das dieses als erstes Mitglied einbettet:

typedef struct {
    int speed;
} Vehicle;

typedef struct {
    Vehicle vehicle;  // Basis als erstes Mitglied
    int doors;
} Car;

Erstellen Sie eine Funktion, die mit dem Basistyp arbeitet:

void accelerate(Vehicle* v, int amount) {
    v->speed += amount;
}

Casten Sie einen Zeiger des abgeleiteten Typs auf den Zeiger des Basistyps, um die Funktion aufzurufen:

Car my_car = {{0}, 4};  // speed=0, doors=4
accelerate((Vehicle*)&my_car, 50);
printf("Speed: %d\n", my_car.vehicle.speed);  // Ausgabe: 50

Dies ermöglicht es Ihnen, wiederverwendbaren Code zu schreiben, der auf dem Basistyp operiert und gleichzeitig jeden abgeleiteten Typ akzeptiert, der diesen einbettet.

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
    // Denke daran: Motorcycle bettet Vehicle als sein erstes Mitglied ein
    
    // TODO: Demonstriere Upcasting durch Aufrufen von boost
    // Caste dein Motorcycle* zu Vehicle*, um es an die boost-Funktion zu übergeben
    
    // TODO: Gib die Informationen des Motorcycle aus
    
    return 0;
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Object Oriented Programming