メソッドのシミュレーション
CoddyのCジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 39/61。
構造体の中に関数ポインタを配置する方法を見てきました。本当の力は、同じ構造体型の異なる instances に異なる関数を割り当てるときに発揮されます。これが、動作の異なるメソッドをシミュレートする方法です。
カスタマイズ可能なロジックを持つアクションを表す構造体を考えてみましょう:
typedef void (*ActionFunc)(void);
typedef struct {
const char* name;
ActionFunc execute;
} Action;instancesを作成すると、それぞれに異なる関数を接続します。
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 を呼び出すと、まったく異なる結果が得られます。この構造体は単にデータを保持しているだけではありません。インスタンスごとに異なる振る舞いを保持しています。
このパターンは、他の言語におけるオブジェクトの仕組みを反映しています。各オブジェクトは、同じインターフェースを共有していても、メソッドを独自に実装できます。
チャレンジ
簡単Speakerシステムを構築して、同じ構造体型の異なるインスタンスが、どのfunctionを割り当てるかによって、まったく異なる振る舞いを示せることを説明しましょう。
コードを整理するために、2つのファイルを作成します。
speaker.h:パラメータを取らず、何も返さないSpeakFuncというfunctionpointer型をDefineします。次に、name(const char*)と、SpeakFunc型のspeakfunction pointerを含むSpeaker構造体をDefineします。main.c:ヘッダーをincludeし、3つの異なる発話functionを実装します。bark:Woof!をprintsしますmeow:Meow!をprintsしますchirp:Chirp!をprintsします
functionに割り当てられた2つのSpeakerインスタンスを作成し、それらのspeakメソッドをCallして、多相的な振る舞いを実際に確認します。
プログラムは2つの入力を受け取ります:first speaker type(barkには1、meowには2、chirpには3)と、second speaker type(こちらも1、2、または3)です。
2つのSpeakerインスタンスをCreateします。名前は自由に付けられます("Dog"、"Cat"、"Bird"、または単に"Speaker1"、"Speaker2"など)。入力コードに基づいて、それぞれを適切なfunctionに割り当て、その後、各インスタンスで順番にspeakをCallします。
first typeが1で、second typeが2の場合のOutput例:
Woof!
Meow!first typeが3で、second typeが3の場合のOutput例:
Chirp!
Chirp!ここでの重要なポイントは、両方のspeakerが同じSpeaker型であるにもかかわらず、それぞれのspeakメンバーに異なるfunctionが割り当てられているため、異なるOutputを生成することです。ヘッダーファイルではinclude guardを使用することを忘れないでください。
自分で試してみよう
#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: 2つの Speaker インスタンスを作成する
// 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;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Cオンラインコンパイラ