ゲッターとセッター
CoddyのCジャーニー「Object Oriented Programming」セクションの一部 — レッスン 19/61。
不透明なポインタを使用すると、構造体のメンバは外部のコードから隠蔽されます。しかし、外部のコードがデータに直接アクセスできない場合、どのようにしてデータを読み取ったり変更したりすればよいのでしょうか?その答えは、ゲッター(getter)およびセッター(setter)関数です。これらは、プライベートなデータへの制御されたアクセスを提供する公開インターフェースです。
ゲッターは、隠蔽されたメンバの値を返します:
// counter.h
int counter_get_value(const Counter *c);
// counter.c
int counter_get_value(const Counter *c) {
return c->value;
}
セッターは、隠蔽されたメンバを変更します:
// counter.h
void counter_set_value(Counter *c, int new_value);
// counter.c
void counter_set_value(Counter *c, int new_value) {
c->value = new_value;
}
命名規則に注目してください:衝突を避け、APIを明確にするために、関数には型名(counter_)をプレフィックスとして付けます。ゲッターはオブジェクトを変更しないため、constポインタを使用します。
これで、外部コードが隠蔽されたデータと安全に対話できるようになります:
// main.c
Counter *c = create_counter(10);
printf("%d\n", counter_get_value(c)); // 10
counter_set_value(c, 25);
printf("%d\n", counter_get_value(c)); // 25
このパターンは、データへのアクセス方法を完全に制御できるようにします。これは、次のレッスンでセッターにバリデーションロジックを追加する際の基礎となります。
チャレンジ
簡単ゲッターとセッター関数を使用した不透明ポインタ(opaque pointer)パターンを用いる Score モジュールを構築しましょう。外部コードが提供されたパブリックインターフェースを通じてのみデータとやり取りできる、隠蔽されたスコアトラッカーを作成します。
以下の3つのファイルを作成します:
score.h: パブリックインターフェースです。前方宣言(構造体の本体なし!)を使用して不透明なScore型を宣言します。以下の関数を宣言してください:create_score— 初期ポイントの整数を受け取り、Score ポインタを返しますfree_score— Score ポインタを受け取り、メモリを解放しますscore_get_points—const Score *を受け取り、現在のポイントを返すゲッターですscore_set_points—Score *と整数を受け取り、ポイントの値を更新するセッターです
SCORE_Hを使用してください。score.c: 隠蔽された単一のメンバint pointsを持つ実際のstruct Scoreを定義します。4つの関数すべてを実装してください。ゲッターは隠蔽された値を返し、セッターはそれを更新する必要があります。ゲッターはオブジェクトを変更しないため、constポインタを使用することを忘れないでください。main.c: スコアを作成し、その値を読み取り、セッターを通じて変更し、再度読み取ることでモジュールを実演します。
2つの入力を受け取ります:初期ポイント(整数)と、設定する新しいポイント値(整数)です。
メインファイルで、初期ポイントを使用して Score を作成し、ゲッターを使用して現在の値を表示し、セッターを使用して新しい値で更新し、更新された値を表示した後、スコアを解放して確認メッセージを表示します。
出力は以下の形式で表示してください:
Initial: {points}
Updated: {points}
Freed例えば、入力が 100 と 250 の場合、出力は以下のようになります:
Initial: 100
Updated: 250
Freedmain.c が points に直接アクセスできず、必ずゲッターとセッター関数を経由しなければならない点に注目してください。これがカプセル化の実践です。隠蔽されたデータがどのように読み取られ、変更されるかを正確に制御できます。
チートシート
不透明ポインタ(opaque pointers)を使用すると、構造体のメンバは外部コードから隠蔽されます。このプライベートデータにアクセスしたり変更したりするには、ゲッター(getter)関数とセッター(setter)関数を使用します。
ゲッターは隠蔽されたメンバの値を返し、オブジェクトを変更しないため const ポインタを使用します。
// counter.h
int counter_get_value(const Counter *c);
// counter.c
int counter_get_value(const Counter *c) {
return c->value;
}
セッターは隠蔽されたメンバを変更します。
// counter.h
void counter_set_value(Counter *c, int new_value);
// counter.c
void counter_set_value(Counter *c, int new_value) {
c->value = new_value;
}
衝突を避け、APIを明確にするために、型名をプレフィックス(counter_)とする命名規則を使用してください。
外部コードは、これらの関数を通じて隠蔽されたデータとやり取りします。
// main.c
Counter *c = create_counter(10);
printf("%d\n", counter_get_value(c)); // 10
counter_set_value(c, 25);
printf("%d\n", counter_get_value(c)); // 25
自分で試してみよう
#include <stdio.h>
#include "score.h"
int main() {
int initial_points, new_points;
scanf("%d", &initial_points);
scanf("%d", &new_points);
// TODO: initial_points を使用して Score を作成する
// TODO: ゲッターを使用して初期値を出力する
// 形式: "Initial: {points}"
// TODO: new_points を使用してセッターでスコアを更新する
// TODO: ゲッターを使用して更新された値を出力する
// 形式: "Updated: {points}"
// TODO: スコアを解放し、"Freed" と出力する
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。