Menu
Coddy logo textTech

アップキャスト

CoddyのCジャーニー「Object Oriented Programming」セクションの一部 — レッスン 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)の一種であるため、Vehicle* を受け取る関数はすべて、オートバイを処理できるはずです。

コードは以下の3つのファイルに分けて構成します:

  • vehicle.h: インクルードガードを使用して構造体の階層を定義します。speed(現在の速度を表す整数)と max_speed(車両の最高速度を表す整数)の2つのフィールドを持つ Vehicle 構造体を作成します。次に、Vehicle を最初のメンバとして埋め込み、has_sidecar フィールド(1は「はい」、0は「いいえ」を表す整数)を追加した Motorcycle 構造体を定義します。2つの関数を宣言します:Vehicle* と速度を上げる量を受け取る boost 関数、および Motorcycle* を受け取る print_motorcycle 関数です。
  • vehicle.c: 両方の関数を実装します。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 はサイドカーの有無を示します。ブーストによって速度が最高速度を超える場合は、速度は最高速度で制限される必要があります。

ここでの重要な学習ポイントは、boost 関数はオートバイについて何も知らないということです。それは Vehicle に対してのみ機能します。しかし、アップキャスティングを通じて、オートバイはこの汎用関数を使用して、埋め込まれた車両データを変更することができます。

チートシート

アップキャスティングは、基本型を埋め込んだ構造体へのポインタを、その基本型を期待する関数に渡すことで、特定の(派生)型をより一般的な(基本)型として扱います。

基本構造体と、それを最初のメンバとして埋め込む派生構造体を定義します:

typedef struct {
    int speed;
} Vehicle;

typedef struct {
    Vehicle vehicle;  // Base as first member
    int doors;
} Car;

基本型を扱う関数を作成します:

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

派生型のポインタを基本型のポインタにキャストして、関数を呼び出します:

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

これにより、基本型を埋め込んだ任意の派生型を受け入れつつ、基本型に対して動作する再利用可能なコードを記述できます。

自分で試してみよう

#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腕試し

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

Object Oriented Programmingのすべてのレッスン