Menu
CoddyTech

アップキャスト

CoddyのCジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 31/61。

アップキャストとは、特定の(派生)型を、より汎用的な(基底)型として扱うことです。Cのコンポジションモデルでは、基底型を埋め込んだ構造体へのポインターを、基底型を受け取る関数に渡すことを意味します。

Vehicle 基底構造体と、それを含む Car を考えてみましょう。

typedef struct {
    int speed;
} Vehicle;

typedef struct {
    Vehicle vehicle;  // 最初のメンバーとしての基底型
    int doors;
} Car;

では、どんな乗り物にも使える汎用関数を想像してみましょう。

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

最初のメンバーに関する規則のおかげで、Car*をVehicle*にキャストすることで、この関数に渡せます。

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

これがアップキャストです。特定の型から一般的な型へ「上がる」ことを意味します。関数 accelerate は、Car を扱っているとは認識せず、Vehicle としてのみ認識します。これにより、基底型を操作しながら、それを埋め込んだ派生型ならどれでも受け取れる、再利用可能なコードを書けます。

challenge icon

チャレンジ

簡単

アップキャストの動作を示す、シンプルな交通システムを作りましょう。基底のVehicle型と、より具体的なMotorcycle型を作成し、ポインターキャストを通じて汎用関数が両方を扱えることを示します。

アップキャストの利点は、基底型用の関数を書けば、その型を埋め込んだ派生型でも問題なく使えることです。あなたのオートバイは乗り物の一種なので、Vehicle*を受け取る関数なら処理できるはずです。

コードを次の3つのファイルに分けて作成します。

  • vehicle.h: インクルードガードを使って構造体の階層を定義します。2つのフィールドを持つVehicle構造体を作成します。speed(現在の速度を表す整数)とmax_speed(乗り物の最高速度を表す整数)です。次に、最初のメンバーとしてVehicleを埋め込み、さらにhas_sidecarフィールド(整数で、1はあり、0はなしを意味します)を追加したMotorcycle構造体を定義します。2つの関数を宣言します。Vehicle*と速度の増加量を受け取るboostと、Motorcycle*を受け取るprint_motorcycleです。
  • vehicle.c: 2つの関数を実装します。boost関数では、指定された量だけ乗り物の速度を増やします。ただし、速度がmax_speedを超える場合は、上限をmax_speedにします。print_motorcycle関数では、埋め込まれた乗り物のデータを含む、オートバイの情報をすべて表示します。
  • main.c: Motorcycleを作成し、すべてのフィールドを初期化します。次に、boost関数を呼び出してアップキャストを実演します。Motorcycle*をVehicle*にキャストして、この汎用関数に渡します。速度を増やした後、オートバイの情報を表示して結果を確認します。

入力として、オートバイの初速度、最高速度、サイドカーの有無(1または0)、および速度の増加量の4つを受け取ります。

出力は次のようになります。

Speed: 75
Max Speed: 120
Sidecar: Yes

ここで、75は速度を増やした後の速度(初速度50 + 増加量25)、120は最高速度です。また、YesまたはNoは、オートバイにサイドカーがあるかどうかを示します。速度の増加によってmax_speedを超える場合、速度はmax_speedを上限とします。

ここでの重要な学びは、boostはオートバイについて何も知らないということです。この関数が扱うのはVehicleだけです。それでもアップキャストによって、オートバイはこの汎用関数を使い、埋め込まれた乗り物のデータを変更できます。

自分で試してみよう

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

int main() {
    int initial_speed, max_speed, has_sidecar, boost_amount;
    
    // 入力を読み取る
    scanf("%d", &initial_speed);
    scanf("%d", &max_speed);
    scanf("%d", &has_sidecar);
    scanf("%d", &boost_amount);
    
    // TODO: Motorcycleを作成し、すべてのフィールドを初期化する
    // 注意: Motorcycleは最初のメンバーとしてVehicleを埋め込んでいる
    
    // TODO: boostを呼び出してアップキャストを実演する
    // Motorcycle*をVehicle*にキャストして、boost関数に渡す
    
    // TODO: バイクの情報を表示する
    
    return 0;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Cオンラインコンパイラ