Menu
Coddy logo textTech

構造体における関数ポインタ

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 型から異なる振る舞いを生み出せます。

challenge icon

チャレンジ

簡単

Notifier システムを構築して、struct が function pointer を通じて独自の動作を持てることを示しましょう。

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

  • notifier.h: const char* parameter を受け取り、何も返さない NotifyFunc という名前の function pointer 型を Define します。次に、nameconst char*)と、NotifyFunc 型の notify function pointer を含む Notifier struct を Define します。
  • main.c: header を include し、2つの notification function を Implement します。
    • alert_notify: 次の形式で message を print します: [ALERT] message
    • info_notify: 次の形式で message を print します: [INFO] message
    その後、Notifier instance を 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 starting

type が 2 で message が "All systems normal" の場合の出力例:

[INFO] All systems normal

header 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;
}
quiz icon腕試し

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

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

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