Menu
Coddy logo textTech

ポリモーフィックな反復処理

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

インターフェースを作成し、それらを異なる具体的な functions で実装する方法を学びました。本当の利点は、異なるオブジェクトのコレクションを統一的に扱えるようになったときに得られます。つまり、どのような具体的な動作が組み込まれているかに関係なく、それらを反復処理し、それぞれに同じ function pointer を呼び出せるのです。

それぞれに独自のexecute関数が割り当てられたAction構造体の配列があると想像してください。

typedef void (*ExecuteFunc)(void);

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

void jump(void) { printf("Jumping!\n"); }
void run(void)  { printf("Running!\n"); }
void rest(void) { printf("Resting...\n"); }

これらのactionsのarrayを作成し、eachを異なるfunctionに接続してから、単純なforループでそれらを反復処理できます。

int main() {
    Action actions[3] = {
        { "Jump", jump },
        { "Run", run },
        { "Rest", rest }
    };
    
    for (int i = 0; i < 3; i++) {
        printf("%s: ", actions[i].name);
        actions[i].execute();
    }
    return 0;
}

ループは、それぞれの Action が内部で何をするかを知る必要も、気にする必要もありません。各要素に対して単に execute() を呼び出します。

これがポリモーフィズムの実例です。同じコードで、まったく異なる振る舞いを持つオブジェクトを処理できます。新しいアクションの種類を追加するために反復処理のロジックを変更する必要はありません。array に別の要素を追加するだけです。

challenge icon

チャレンジ

簡単

Task 管理システムを構築して、ポリモーフィックな反復処理を示しましょう。これは、共通のインターフェースを共有しながら異なる動作を実行するオブジェクトのコレクションを処理するものです。

コードを3つのファイルに分けて整理します。

  • task.h:ここでタスクのインターフェースを Define します。パラメータを受け取らず、nothing を返す、TaskFunc という名前の function pointer type を Create します。次に、nameconst char*)と、TaskFunc 型の run function pointer を containing する Task struct を Define します。
  • task.c:異なる種類の作業を表す3つの異なるタスク functions を Implement します。
    • backup_taskBacking up data... を print します
    • cleanup_taskCleaning up files... を print します
    • report_taskGenerating report... を print します
  • main.c:ここですべてをまとめます。異なるタスク function にそれぞれ接続された Task struct の array を Create します。次に array を Iterate し、各タスクをポリモーフィックに execute します。各タスクについて、その名前を print してから run function を call します。

プログラムは、実行するタスクの(1、2、または3)を1つの入力として受け取ります。

次の order で、ちょうど3つのタスクを containing する array を Create します。Backup という名前のバックアップタスク、Cleanup という名前のクリーンアップタスク、Report という名前のレポートタスクです。入力された count に基づき、array の先頭からその数だけのタスクのみを Iterate します。

Iterate 中の各タスクについて、タスクの名前に続けてコロンとスペースを print し、その後で run function を call します。

入力が 2 の場合の出力例:

Backup: Backing up data...
Cleanup: Cleaning up files...

入力が 3 の場合の出力例:

Backup: Backing up data...
Cleanup: Cleaning up files...
Report: Generating report...

重要なポイントは、loop が各タスクの動作を知る必要がないことです。単に名前を print し、各要素で run() を call するだけです。同じ反復コードで、すべてのタスクの種類を一様に処理できます。header file では include guards を使用することを忘れないでください。

自分で試してみよう

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

// task.c に実装されたタスク関数を宣言する
void backup_task(void);
void cleanup_task(void);
void report_task(void);

int main() {
    int count;
    scanf("%d", &count);
    
    // TODO: この順序で 3 つの Task 構造体の配列を作成する:
    // 1. backup_task を使用した "Backup" という名前のバックアップタスク
    // 2. cleanup_task を使用した "Cleanup" という名前のクリーンアップタスク
    // 3. report_task を使用した "Report" という名前のレポートタスク
    
    // TODO: 配列から 'count' 個のタスクを反復処理する
    // 各タスクについて、"<name>: " を出力してからその run 関数を呼び出す
    
    return 0;
}
quiz icon腕試し

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

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

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