不透明な構造体の定義
CoddyのCジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 18/61。
opaque ポインターの概念を理解したところで、パターンを完成させる方法を見てみましょう。ヘッダーでは型が存在することを宣言しますが、ソースファイルでは実際にその中身を定義します。
完全なセットアップは次のとおりです。まず、前方宣言だけを含むヘッダです。
// counter.h
#ifndef COUNTER_H
#define COUNTER_H
typedef struct Counter Counter; // 不完全型
Counter *create_counter(int start);
void free_counter(Counter *c);
#endif
次に、ソースファイルにはactual 構造体定義が含まれています:
// counter.c
#include "counter.h"
#include <stdlib.h>
struct Counter {
int value; // 外部から隠されています!
int step;
};
Counter *create_counter(int start) {
Counter *c = malloc(sizeof(Counter));
c->value = start;
c->step = 1;
return c;
}
void free_counter(Counter *c) {
free(c);
}
重要なポイント:struct Counter { ... } は .c ファイル内にのみ記述されます。counter.h を include するファイルは Counter が存在することは認識しますが、value や step を見ることはできません。main.c で c->value と書こうとすると、コンパイラーエラーが発生します。members は完全に不可視だからです。
この分離は、Cにおけるカプセル化の基盤です。次のステップは、getter関数とsetter関数を通じて制御されたアクセスを提供することです。
チャレンジ
簡単Timerモジュールを作成して、opaque pointer patternを実演しましょう。structの定義をソースファイル内に隠し、モジュールを使用するコードから内部のmembersを完全に見えなくする方法を練習します。
3つのファイルを作成します。
timer.h:これは公開インターフェースです。forward declarationのみを使用してopaqueなTimer型をDeclareします。ここにはstruct bodyを記述しません。また、2つのfunctionもDeclareします。create_timer(seconds用のintegerを受け取り、Timer pointerをreturns)とfree_timer(Timer pointerを受け取り、後片付けを行う)です。symbolTIMER_Hを使用してinclude guardsを記述します。timer.c:ここに秘密の実装があります。2つのHiddenなmembers、int secondsとint running(runningには1、stoppedには0を使用)を持つactualなstruct TimerをDefineします。structをAllocateし、secondsをprovidedの値でInitializeし、runningを1に設定するconstructorをImplementします。memoryをFreeするdestructorもImplementします。main.c:Timer moduleを使用してtimerを作成し、正常にcreatedされたことを確認します。structのmembersはHiddenなので、直接アクセスすることはできません。これがこの仕組みの目的です。作成を確認するmessageをPrintし、その後timerをFreeしてcleanup messageをPrintします。
入力として、timerをInitializeするsecondsの数(integer)を1つ受け取ります。
main fileで、providedのsecondsの値を使ってtimerを作成します。creation messageをPrintし、その後timerをFreeしてconfirmationをPrintします。
次の形式でoutputをPrintします。
Timer created
Timer freedここでの重要な洞察は、main.cはTimer pointersを扱えますが、secondsやrunningに直接アクセスする方法はないということです。main.cでt->secondsを書こうとすると、struct bodyはtimer.cでのみDefineされているため、compiler errorが発生します。これがtrue encapsulationです。implementation detailsは、モジュールのusersから完全にHiddenになります。
自分で試してみよう
#include <stdio.h>
#include "timer.h"
int main() {
int seconds;
scanf("%d", &seconds);
// TODO: 提供された秒数の値でタイマーを作成する
// TODO: Print "Timer created"
// TODO: タイマーを解放する
// TODO: "Timer freed" を出力する
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Cオンラインコンパイラ