Menu
Coddy logo textTech

アップキャスト

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

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

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* を渡せます。

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 型を作成し、ポインターキャストを通じて汎用関数が両方を扱えることを示します。

アップキャストの利点は、基本型用に関数を記述すると、それを埋め込んでいる派生型ならどれでもシームレスに動作することです。あなたの motorcycle は "is a" vehicle なので、Vehicle* を期待する関数なら、それを処理できるはずです。

コードを3つのファイルに分けて整理します。

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

4つの入力を受け取ります。motorcycle の初期速度最高速度サイドカーがあるかどうか(1または0)、そして速度の増加量です。

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

Speed: 75
Max Speed: 120
Sidecar: Yes

ここで、75 は速度を増加させた後の速度(初期値50 + 増加量25)、120 は最高速度、Yes または No は motorcycle にサイドカーがあるかどうかを示します。増加によって速度が max_speed を超える場合、速度は max_speed に制限されます。

ここでの重要な学習ポイントは、boost が motorcycle について何も知らないということです。この関数は Vehicle のみを扱います。それでもアップキャストによって、motorcycle はこの汎用関数を使い、埋め込まれた車両データを変更できます。

自分で試してみよう

#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オンラインコンパイラ