構造体における関数ポインタ
CoddyのCジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 38/61。
これまで、functionポインタをスタンドアロンの変数として使用したり、引数として渡したりしてきました。ポリモーフィズムに向けた次のステップは、functionポインタをstructの内部に直接埋め込み、structに独自の振る舞いを持たせることです。
構造体に function pointer が含まれている場合、それぞれの instance は異なる function を保持できます。つまり、同じ型の 2 つのオブジェクトでも、function pointer を呼び出すと異なる動作をする可能性があります。
typedef void (*PrintFunc)(const char*);
typedef struct {
const char* name;
PrintFunc print; // メンバとしての関数ポインタ
} Printer;Printer 構造体には現在、2つのメンバーがあります。データ(name)と振る舞い(print)です。これを使用するには、初期化時にポインターへ関数を assign します。
void loud_print(const char* msg) {
printf("!!! %s !!!\n", msg);
}
int main() {
Printer p;
p.name = "Alert";
p.print = loud_print;
p.print("Hello"); // 出力: !!! Hello !!!
return 0;
}構造体を通じてfunctionを呼び出していることに注目してください:p.print("Hello")。これは、他の言語でオブジェクトのメソッドを呼び出す場合と非常によく似ています。構造体には、そのデータと、それを操作するfunctionの両方が含まれています。
このパターンは、Cにおけるポリモーフィズムの基盤です。異なるインスタンスに異なる function を割り当てることで、同じ struct 型から異なる振る舞いを生み出せます。
チャレンジ
簡単Notifier システムを構築して、struct が function pointer を通じて独自の動作を持てることを示しましょう。
コードを整理するために、2つのファイルを作成します。
notifier.h:const char*parameter を受け取り、何も返さないNotifyFuncという名前の function pointer 型を Define します。次に、name(const char*)と、NotifyFunc型のnotifyfunction pointer を含むNotifierstruct を Define します。main.c: header を include し、2つの notification function を Implement します。alert_notify: 次の形式で message を print します:[ALERT] messageinfo_notify: 次の形式で message を print します:[INFO] message
Notifierinstance を Create し、input に Based して appropriate な function を接続し、struct の function pointer を通じて notification を Call します。
プログラムは2つの input を受け取ります。notification type(1 は alert、2 は info)と、表示する message です。
type に Based して対応する function を notifier の notify member に assign し、その後 struct を通じて Call して message を表示します。
type が 1 で message が "System starting" の場合の出力例:
[ALERT] System startingtype が 2 で message が "All systems normal" の場合の出力例:
[INFO] All systems normalheader file では include guard を使用し、struct member を通じて function を Call することを忘れないでください: n.notify(message)
自分で試してみよう
#include <stdio.h>
#include "notifier.h"
// TODO: alert_notify 関数を実装する
// 次のように出力する: [ALERT] message
// TODO: info_notify 関数を実装する
// 次のように出力する: [INFO] message
int main() {
int type;
char message[256];
// 入力を読み取る
scanf("%d", &type);
getchar(); // 改行を消費する
fgets(message, sizeof(message), stdin);
// 存在する場合、message から末尾の改行を削除する
int len = 0;
while (message[len] != '\0') len++;
if (len > 0 && message[len-1] == '\n') message[len-1] = '\0';
// TODO: Notifier のインスタンスを作成する
// TODO: type に基づいて (1 は alert、2 は info)、
// 適切な関数を notifier の notify メンバーに割り当てる
// TODO: 構造体の関数ポインタを通じて通知を呼び出す
// 使用: n.notify(message)
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Cオンラインコンパイラ