10-2 存取控制——public 與 private

一、class 裡的東西，不是誰都可以動的
先看一個銀行帳戶的例子。
#include <iostream>

using namespace std;

class BankAccount {
public:
 string owner;
 int balance;
};

int main() {
 BankAccount acc;
 acc.owner = "小明";
 acc.balance = 1000;

 acc.balance = -99999; // 沒有任何阻擋，帳戶餘額變成負的！

 cout << acc.balance << endl;
}

這是因為我們之前在 class 裡用了 public: 這個存取修飾詞。 public 的意思就是「公開的」，誰想看、誰想改都可以。
用 private: 將存取限制改為「私有的」看看差別。
#include <iostream>

using namespace std;

class BankAccount {
private:
 string owner;
 int balance;
};

int main() {
 BankAccount acc;
 acc.owner = "小明";
 acc.balance = 1000;

 acc.balance = -99999; // 沒有任何阻擋，帳戶餘額變成負的！

 cout << acc.balance << endl;
}

修改之後，連編譯都失敗，出現了大量如下的錯誤訊息。
因為使用 privte 修飾的屬性和成員函數，在該 class 之外(第 5~9 行之外)都無法存取，在編譯時就攔下了不合法的存取。
main.cpp:13:9: error: 'std::string BankAccount::owner' is private within this context
 13 | acc.owner = "小明";
 | ^~~~~
main.cpp:7:12: note: declared private here
 7 | string owner;
 | ^~~~~
main.cpp:14:9: error: 'int BankAccount::balance' is private within this context
 14 | acc.balance = 1000;
 | ^~~~~~~
main.cpp:8:9: note: declared private here
 8 | int balance;
 | ^~~~~~~
main.cpp:16:9: error: 'int BankAccount::balance' is private within this context
 16 | acc.balance = -99999; // 沒有任何阻擋，帳戶餘額變成負的！
 | ^~~~~~~
main.cpp:8:9: note: declared private here
 8 | int balance;
 | ^~~~~~~
main.cpp:18:17: error: 'int BankAccount::balance' is private within this context
 18 | cout << acc.balance << endl;
 | ^~~~~~~
main.cpp:8:9: note: declared private here
 8 | int balance;
 | ^~~~~~~

二、提供外界有限的操作
設成 public 太危險，設成 private 又動不了，怎麼辦呢?
我們可以

把內部屬性設為 private
用 public 開放幾個成員函數，給外界有限能力操作內部屬性

#include <iostream>

using namespace std;

class BankAccount {
private:
 string owner;
 int balance; // 外部無法直接存取

public:
 // 以下成員函數，開放給外部使用，提供存取 balance 的管道
 bool withdraw(int amount) {
 if (amount > balance) {
 cout << "餘額不足！" << endl;
 return false;
 }
 balance -= amount;
 return true;
 }

 void deposit(int amount) {
 if (amount <= 0) {
 cout << "存款金額必須大於 0！" << endl;
 return;
 }
 balance += amount;
 }

 int getBalance() { return balance; }
};

int main()
{
 BankAccount account;
 account.deposit(1000); // 存款 1000 元
 cout << "目前餘額: " << account.getBalance() << " 元" << endl;

 if (account.withdraw(500)) { // 提款 500 元
 cout << "提款成功！" << endl;
 }
 cout << "目前餘額: " << account.getBalance() << " 元" << endl;

 if (!account.withdraw(600)) { // 嘗試提款 600 元，應該會失敗
 cout << "提款失敗！" << endl;
 }
 cout << "目前餘額: " << account.getBalance() << " 元" << endl;

 return 0;
}

現在外部程式只能透過 withdraw() 和 deposit() 來改變餘額，透過 getBalance() 來取得餘額，非法操作會被擋下來。
public、private 存取修飾詞的作用範圍
class Example {
// 這裡沒寫 → class 預設是 private
 int secret;

public:
 int visible;
 void doSomething() { }

private:
 int alsoSecret;
};

public: 和 private: 後面的所有成員，都套用該修飾子，直到遇到下一個修飾子為止。
三、封裝(encapsulate)的概念
有時候我們不希望 class 的使用者直接去更動某些 data member 的資料，這時我們會將這些 data member 宣告在 private 區域中，並在 public 區域中提供 member function 來操作這些 data member。
以手錶為例，一般來說製表師傅不會希望使用者自己去撥動錶內的齒輪，所以他會把手錶內部的複雜機構用錶殼緊緊的封裝起來，不過他還是會提供一些按鈕或旋鈕來讓我們調整時間。

private – 手錶的內部複雜機構
public – 調整時間的按鈕或旋鈕

當我們試著在非成員函數裡存取 private 的 data member 或 member function 時，在編譯時期就會出現錯誤訊息。
這就是「物件導向程式設計」中很重要的「封裝」概念。我們設計了一個很精巧的 class，包含了很多的屬性和成員函數，但是對外界來說，它被一個「盒子」封裝起來，你只操作這個盒子外漏的那幾個界面。

