Menu
Coddy logo textTech

Konwersja w górę hierarchii

Część sekcji Programowanie obiektowe ścieżki C w Coddy. Lekcja 31 z 61.

Rzutowanie w górę polega na traktowaniu konkretnego typu (pochodnego) jako jego bardziej ogólnego typu (bazowego). W modelu kompozycji języka C oznacza to przekazanie do funkcji oczekującej typu bazowego wskaźnika do struktury, która osadza typ bazowy.

Rozważ bazową strukturę Vehicle i strukturę Car, która ją zawiera:

typedef struct {
    int speed;
} Vehicle;

typedef struct {
    Vehicle vehicle;  // Struktura bazowa jako pierwsze pole
    int doors;
} Car;

Teraz wyobraź sobie funkcję ogólną, która działa z dowolnym pojazdem:

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

Dzięki regule pierwszego elementu członkowskiego możemy przekazać do tej funkcji Car*, rzutując go na Vehicle*:

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

To jest rzutowanie w górę: przechodzimy „w górę” od konkretnego typu do ogólnego. Funkcja accelerate nie wie, że działa z obiektem typu Car; widzi tylko obiekt typu Vehicle. Dzięki temu możesz pisać kod wielokrotnego użytku, który działa na typie bazowym, przyjmując dowolny typ pochodny, który go osadza.

challenge icon

Wyzwanie

Łatwy

Zbudujmy prosty system transportu, który pokazuje działanie rzutowania w górę. Utworzysz bazowy typ Vehicle i bardziej szczegółowy typ Motorcycle, a następnie pokażesz, jak ogólna funkcja może obsługiwać oba typy za pomocą rzutowania wskaźników.

Zaletą rzutowania w górę jest to, że możesz pisać funkcje dla typu bazowego, a będą one bez problemu działać z każdym typem pochodnym, który go zawiera. Twój motocykl „jest” pojazdem, więc każda funkcja oczekująca Vehicle* powinna móc go przetworzyć.

Rozmieścisz swój kod w trzech plikach:

  • vehicle.h: Zdefiniuj hierarchię struktur z zabezpieczeniami przed wielokrotnym dołączeniem. Utwórz strukturę Vehicle z dwoma polami: speed (liczbą całkowitą określającą aktualną prędkość) i max_speed (liczbą całkowitą określającą maksymalną prędkość pojazdu). Następnie zdefiniuj strukturę Motorcycle, która zawiera Vehicle jako pierwsze pole i dodaje pole has_sidecar (liczbę całkowitą, gdzie 1 oznacza tak, a 0 — nie). Zadeklaruj dwie funkcje: boost, która przyjmuje Vehicle* i wartość, o którą należy zwiększyć prędkość, oraz print_motorcycle, która przyjmuje Motorcycle*.
  • vehicle.c: Zaimplementuj obie funkcje. Funkcja boost powinna zwiększać prędkość pojazdu o podaną wartość, ale ograniczać ją do max_speed; jeśli nowa prędkość przekroczyłaby maksimum, ustaw ją na max_speed. Funkcja print_motorcycle powinna wyświetlać wszystkie informacje o motocyklu, w tym dane zawartej w nim struktury pojazdu.
  • main.c: Utwórz obiekt Motorcycle i zainicjalizuj wszystkie jego pola. Następnie zademonstruj rzutowanie w górę, wywołując funkcję boost: zrzuć Motorcycle* na Vehicle*, aby przekazać go do tej ogólnej funkcji. Po zwiększeniu prędkości wyświetl informacje o motocyklu, aby zobaczyć wynik.

Otrzymasz cztery dane wejściowe: prędkość początkową motocykla, jego maksymalną prędkość, informację, czy ma boczny wózek (1 lub 0), oraz wartość zwiększenia prędkości.

Wynik powinien wyglądać tak:

Speed: 75
Max Speed: 120
Sidecar: Yes

Gdzie 75 to prędkość po zwiększeniu (początkowe 50 + zwiększenie o 25), 120 to maksymalna prędkość, a Yes lub No wskazuje, czy motocykl ma boczny wózek. Jeśli zwiększenie prędkości spowodowałoby przekroczenie max_speed, prędkość powinna zostać ograniczona do max_speed.

Najważniejsze jest to, że funkcja boost nic nie wie o motocyklach. Obsługuje wyłącznie Vehicle. Dzięki rzutowaniu w górę motocykl może jednak użyć tej ogólnej funkcji, aby zmodyfikować zawarte w nim dane pojazdu.

Spróbuj swoich sił

#include <stdio.h>
#include "vehicle.h"

int main() {
    int initial_speed, max_speed, has_sidecar, boost_amount;
    
    // Wczytaj dane wejściowe
    scanf("%d", &initial_speed);
    scanf("%d", &max_speed);
    scanf("%d", &has_sidecar);
    scanf("%d", &boost_amount);
    
    // TODO: Utwórz Motorcycle i zainicjalizuj wszystkie jego pola
    // Pamiętaj: Motorcycle zawiera Vehicle jako swój pierwszy element
    
    // TODO: Zademonstruj rzutowanie w górę przez wywołanie boost
    // Rzutuj Motorcycle* na Vehicle*, aby przekazać go do funkcji boost
    
    // TODO: Wyświetl informacje o motocyklu
    
    return 0;
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator C online