アクセス修飾子の詳細
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 35/104。
3つのアクセス修飾子について理解したところで、同じクラスのオブジェクトが複数ある場合に、それらがどのように動作するかを見ていきましょう。よくある誤解は、privateが「このオブジェクトのみ」を意味するというものですが、実際には「このクラスのみ」を意味します。
同じクラスのオブジェクトは、お互いの非公開(private)メンバにアクセスできます:
class Person {
std::string secret;
public:
Person(std::string s) : secret(s) {}
void readSecret(const Person& other) {
// 他のオブジェクトの非公開メンバへのアクセス - 許可されています!
std::cout << other.secret;
}
};これは、アクセス制御がオブジェクトレベルではなく、クラスレベルで強制されるためです。readSecret メソッドは Person クラスの一部であるため、任意の Person オブジェクトのプライベートメンバにアクセスできます。この設計により、コピーコンストラクタや比較演算子などの操作が、他のオブジェクトの内部状態を扱えるようになります。
クラス内でアクセス修飾子を複数回切り替えることもできます:
class Mixed {
public:
void publicMethod1();
private:
int privateData;
public:
void publicMethod2(); // 再びpublicへ
private:
void privateHelper(); // 再びprivateへ
};これは有効なC++ですが、可読性のために、一般的にはアクセスレベルごとにメンバーをグループ化するのがより良い方法です。ほとんどのスタイルガイドでは、publicメンバーを最初に記述し(これらがクラスのインターフェースを形成するため)、その後にprotected、次にprivateと続けることを推奨しています。
チャレンジ
簡単同じクラスのオブジェクトが互いのプライベートメンバにどのようにアクセスできるかを示す、ウォレット比較システムを構築しましょう。これはC++のアクセス制御に関する重要な洞察です。アクセス制御はオブジェクトレベルではなく、クラスレベルで強制されます。
コードを整理するために、2つのファイルを作成します:
Wallet.h: プライベートな財務データを保存しつつ、ウォレット間の比較を可能にするWalletクラスを定義します。クラスには以下を含める必要があります:- プライベートメンバ:
ownerName(string) とbalance(double) - 所有者名と初期残高を受け取るコンストラクタ
- 所有者の名前を返すパブリックなゲッター
getOwnerName() - このウォレットの残高を別のウォレットのプライベートな残高と比較し、このウォレットの方がお金が多い場合に
trueを返すメソッドhasMoreThan(const Wallet& other) - 両方のウォレットのプライベートな残高の合計を返すメソッド
combinedBalance(const Wallet& other) - このウォレットから別のウォレットにお金を移動させるメソッド
transferTo(Wallet& other, double amount)。これは両方のウォレットのプライベートな残高を直接変更する必要があります(金額が正であり、このウォレットの残高を超えない場合に限ります)。
- プライベートメンバ:
main.cpp: オブジェクト間のプライベートメンバへのアクセスを実演します。入力から2つの所有者名と2つの残高を読み取り(name1, balance1, name2, balance2 の4行)、以下の処理を行います:- 入力値を使用して2つの
Walletオブジェクトを作成します combinedBalance()を使用して"Combined wealth: "Combined wealth: $と出力します"lt;amount>" - ウォレットを比較し、残高が多い方の所有者について
"<name> has more money"と出力します(判定にはhasMoreThan()を使用してください) transferTo()を使用して、最初のウォレットから2番目のウォレットへ100.0を送金します"After transfer:"と出力します- 再び
"Combined wealth: "Combined wealth: $と出力します(値は変わっていないはずです)"lt;amount>" - 再度比較し、
"<name> has more money"と出力します(結果が変わっている可能性があります)
- 入力値を使用して2つの
ここでの重要な概念は、hasMoreThan() や transferTo() といったメソッドが、別の Wallet オブジェクトのプライベートな balance メンバに直接アクセスできるということです。ゲッターは必要ありません。これは、C++のアクセス制御がクラスベースであるためです。Wallet クラス内のコードであれば、どの Wallet オブジェクトのプライベートメンバにもアクセスできます。
すべての通貨価値は、<iomanip> の std::fixed と std::setprecision(2) を使用して小数点以下2桁でフォーマットしてください。残高の入力は std::stod() を使用して変換してください。
チートシート
C++におけるアクセス制御は、オブジェクトレベルではなくクラスレベルで適用されます。これは、同じクラスのオブジェクト同士であれば、お互いの非公開(private)メンバにアクセスできることを意味します:
class Person {
std::string secret;
public:
Person(std::string s) : secret(s) {}
void readSecret(const Person& other) {
// 他のオブジェクトのprivateメンバへのアクセス - 許可されています!
std::cout << other.secret;
}
};readSecretメソッドは、Personクラスの一部であるため、任意のPersonオブジェクトの非公開メンバであるsecretにアクセスできます。この設計により、コピーコンストラクタや比較演算子などの操作が可能になります。
クラス内では、アクセス修飾子を何度でも切り替えることができます:
class Mixed {
public:
void publicMethod1();
private:
int privateData;
public:
void publicMethod2(); // 再びpublicへ
private:
void privateHelper(); // 再びprivateへ
};ただし、可読性の観点からは、メンバをアクセスレベルごとにグループ化するのがより良い習慣です。ほとんどのスタイルガイドでは、最初にpublicメンバを記述し、次にprotected、最後にprivateを記述することを推奨しています。
自分で試してみよう
#include <iostream>
#include <string>
#include <iomanip>
#include "Wallet.h"
using namespace std;
int main() {
// 入力を読み込む: name1, balance1, name2, balance2
string name1, name2;
string balanceStr1, balanceStr2;
getline(cin, name1);
getline(cin, balanceStr1);
getline(cin, name2);
getline(cin, balanceStr2);
double balance1 = stod(balanceStr1);
double balance2 = stod(balanceStr2);
// 通貨価値のための出力フォーマットを設定する
cout << fixed << setprecision(2);
// TODO: 入力値を使用して2つの Wallet オブジェクトを作成する
// TODO: combinedBalance() を使用して合計資産を出力する
// フォーマット: "Combined wealth: $<amount>"
// TODO: hasMoreThan() を使用してウォレットを比較し、どちらがより多くのお金を持っているか出力する
// フォーマット: "<name> has more money"
// TODO: transferTo() を使用して、最初のウォレットから2番目のウォレットへ 100.0 を送金する
// TODO: "After transfer:" を出力する
// TODO: 合計資産を再度出力する
// TODO: 再度比較し、どちらがより多くのお金を持っているか出力する
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタ引数付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ詳解Rule of Three / Five / Zeroまとめ - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () 演算子のオーバーロード型変換演算子まとめ:Matrix クラス