アップキャスト
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 としてのみ認識します。これにより、基底型を操作しながら、それを埋め込んだ派生型ならどれでも受け取れる、再利用可能なコードを書けます。
チャレンジ
簡単アップキャストの動作を示す、シンプルな交通システムを作りましょう。基底の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;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Cオンラインコンパイラ