アップキャスト
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 だけを認識します。これにより、基底型で動作し、それを埋め込む任意の派生型を受け入れる再利用可能なコードを記述できます。
チャレンジ
簡単アップキャストを実際に示す、シンプルな交通システムを構築しましょう。基本となる 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.c:Motorcycleを作成し、そのすべてのフィールドを初期化します。次に、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;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Cオンラインコンパイラ