Menu
CoddyTech

メソッドのシミュレーション

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

構造体の中に関数ポインターを配置する方法を見てきました。本当の威力は、同じ構造体型の異なるインスタンスに異なる関数を割り当てることで発揮されます。これによって、動作の異なるメソッドをシミュレートできます。

カスタマイズ可能なロジックを持つアクションを表す構造体を考えてみましょう。

typedef void (*ActionFunc)(void);

typedef struct {
    const char* name;
    ActionFunc execute;
} Action;

インスタンスを作成するとき、それぞれに異なる関数を割り当てます。

void say_hello(void) {
    printf("Hello!\n");
}

void say_goodbye(void) {
    printf("Goodbye!\n");
}

int main() {
    Action greet = { "Greet", say_hello };
    Action farewell = { "Farewell", say_goodbye };
    
    greet.execute();    // 出力: Hello!
    farewell.execute(); // 出力: Goodbye!
    return 0;
}

greetとfarewellはどちらも同じ型(Action)ですが、それぞれに対してexecuteを呼び出すと、まったく異なる結果が得られます。この構造体は単にデータを保持するだけではありません。インスタンスごとに異なる振る舞いも保持します。

このパターンは、他の言語におけるオブジェクトの仕組みを反映しています。同じインターフェースを共有していても、各オブジェクトはメソッドに独自の実装を持つことができます。

challenge icon

チャレンジ

簡単

どの関数を割り当てるかによって、同じ構造体型の異なるインスタンスがまったく異なる動作を示せることを実演する、Speaker システムを作りましょう。

コードを整理するために、2つのファイルを作成します。

  • speaker.h: 引数を取らず、何も返さない関数ポインター型 SpeakFunc を定義します。次に、name(const char*)と、型が SpeakFunc の speak 関数ポインターを含む Speaker 構造体を定義します。
  • main.c: ヘッダーファイルをインクルードし、発声する関数を3つ実装します。
    • bark: Woof! を出力します。
    • meow: Meow! を出力します。
    • chirp: Chirp! を出力します。
    次に、入力に基づいてそれぞれ異なる関数を割り当てた2つの Speaker インスタンスを作成し、それぞれの speak メソッドを呼び出して、ポリモーフィックな動作を確認します。

プログラムには2つの入力が渡されます。1つ目は1つ目のスピーカーの種類(1は bark、2は meow、3は chirp)、2つ目は2つ目のスピーカーの種類(同じく1、2、または3)です。

2つの Speaker インスタンスを作成します。好きな名前を付けて構いません(たとえば、"Dog"、"Cat"、"Bird"、または単に "Speaker1"、"Speaker2")。入力コードに基づいて、それぞれに適切な関数を割り当て、その後、各インスタンスの speak を順番に呼び出します。

1つ目の種類が1、2つ目の種類が2の場合の出力例:

Woof!
Meow!

1つ目の種類が3、2つ目の種類も3の場合の出力例:

Chirp!
Chirp!

ここで重要なのは、どちらのスピーカーも同じ Speaker 型でありながら、それぞれの speak メンバーに異なる関数が割り当てられているため、異なる出力を生成するという点です。ヘッダーファイルではインクルードガードを使うことを忘れないでください。

自分で試してみよう

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

// TODO: "Woof!" を出力する bark 関数を実装する

// TODO: "Meow!" を出力する meow 関数を実装する

// TODO: "Chirp!" を出力する chirp 関数を実装する

int main() {
    int type1, type2;
    scanf("%d", &type1);
    scanf("%d", &type2);
    
    // TODO: Speaker インスタンスを2つ作成する
    
    // TODO: type1 に基づいて、最初の speaker の speak 関数を設定する
    // (1 = bark, 2 = meow, 3 = chirp)
    
    // TODO: type2 に基づいて、2番目の speaker の speak 関数を設定する
    // (1 = bark, 2 = meow, 3 = chirp)
    
    // TODO: 各 speaker の speak を順番に呼び出す
    
    return 0;
}
quiz icon腕試し

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

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

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