# 10-類別(class)



# 10-1 類別(class)與物件(object)

## 一、物件（object）與類別（class）

在 C++ 中物件和類別有很嚴謹的定義，我們在這裡僅使用簡單例子來做介紹。

什麼是物件(Object)？你的iPhone是個物件、你現在坐著的這張椅子是個物件，小狗小黃也是個物件。

那類別呢(Class)？你的iPhone是個物件，隔壁同學的HTC One也是個物件，而它們同屬於「手機」這個類別。小黃是個物件、小黑也是個物件，牠們同屬於「狗」這個類別。 

## 二、建立類別與產生物件

在產生物件之前必須先建立類別，在C++中建立類別要使用到 class 這個關鍵字，一個最簡單的類別長這個模樣(注意：最後有一個分號)。

```c++
class 類別名稱 {
};
```

假設我們要建立一個dog類別，可以這樣寫。

```c++
class dog {
};
```

接下來我們可以這樣產生兩個 dog 類別的物件：shiro（小白） 和 kuro（小黑）。

```c++
dog shiro;
dog kuro;
```

或

```c++
dog shiro, kuro;
```

就像在定義 int 或 float 等原生型別變數一樣。

## 三、屬性 (attribute)/資料成員(data member)

前面這個 dog類別是一個很單純的類別，它產生的兩個物件也沒什麼用處。現在我們要給它一點變化，讓狗有不同的顏色和叫聲。

```c ++
class dog {
  public:
    string color;
    string sound;
};
```

public 關鍵字表示下面出現的屬性都是「公開的」，你可以在程式的任何地方來改變其值。

在這個例子中我們有兩個 string 型別的屬性 color 和 sound。我們可以用 ` 物件名稱.屬性名稱 ` 的型式來設定或取用特定物件的某個屬性值。

```c++
dog shiro;
dog kuro;

shiro.color = "白色";
kuro.color = "黑色";

cout << "shiro 的顏色是" << shiro.color << endl;
cout << "kuro 的顏色是" << kuro.color << endl;
```

## 四、成員函數(member function)

到目前為止，你會覺得 class 和之前學的 struct 一樣。

但是除了屬性之外，我們還可以在類別中加入成員函數，讓該類別的物件可以「做些事情」，例如：我們可以在 dog 類別中加入 bark 這個成員函數，讓小白和小黑可以叫。

```c++
class dog {
  public:
    string color;
    string sound;

    void bark() {               
        cout << sound << endl;
    }                             
};

int main() 
{
    dog shiro, kuro;

    shiro.color = "白色";
    shiro.sound = "汪！";
    kuro.color = "黑色";
    kuro.sound = "汪汪汪！";

    cout << "shiro 咬他！" << endl;
    shiro.bark();                    // 汪！
    cout << "kuro 咬他！" << endl;
    kuro.bark();                     // 汪汪汪！

    return 0;
}
```

## 五、特殊的成員函數- 建構(constructor), 解構(destructor)

constructor 是一個和類別同名的成員函數，在一個物件被產生時會自動被呼叫，而且 **`沒有傳回值`**

```c++
class dog {
  public:
    string color;
    string sound;

    void bark() {
        cout << sound << endl;;
    }
    dog() {                                     
        cout << "有一隻小狗誕生了！" << endl;
    }                                            
};

int main() 
{
    dog shiro, kuro;

    return 0;
}
```

一個類別可以有很多個 constructor，每個 constructor 的參數列都必須不一樣，在產生物件時，根據你使用參數的不同，相對應的 constructor 會自動被呼叫。

沒有參數的 constructor 稱為 **`default constructor`**。

```c++
class dog {
  public:
    string color;
    string sound;

    void bark() {
        cout << sound << endl;;
    }
    dog() {
        cout << "有一隻小狗誕生了！" << endl;
    }
    dog(string _color, string _sound) {                               
        color = _color;                                                   
        sound = _sound;                                                   
        cout << "有一隻" << color << "小狗誕生了！" << sound << endl;
    }                                                                        
};

int main() 
{
    dog kuro;
    dog shiro("白色","汪！");       

    return 0;
}
```

destructor 是物件「死亡」時會被自動呼叫的成員函數，每個類別只有一個 destructor，它的名字是 ~類別名稱，**`destructor 沒有參數也沒有傳回值`**。

```c++
class dog {
  public:
    string color;
    string sound;
    string name;

    void bark() {
        cout << sound << endl;;
    }
   
    dog(string _name, string _color, string _sound) {
        name = _name;
        color = _color;
        sound = _sound;
        cout << "有一隻名為 " << name << " 的 " << color << " 小狗誕生了！" 
                << sound << endl;
    }
    ~dog() {                                            
        cout << name << " 上天堂了。" << endl;      
    }                                                    
};

int main() 
{
    dog shiro("小白", "白色", "汪！");
    dog *kuro = new dog("小黑", "黑色","喵！");

    delete kuro;

    return 0;
}
```

## 六、使用指標操作物件

使用指標操作物件與使用指標操作 struct 變數一樣，必須用到「->」。

```c++
int main() 
{
    dog *kuro = new dog("小黑", "黑色","喵！");

    kuro->sound = "汪！汪！";                       
    kuro->bark();                                    

    delete kuro;

    return 0;
}
```

## 七、this 指標

this 是一個指向「自己這個物件」的指標。

成員函數的參數名稱和屬性名稱完全一樣時，不加 this-> 編譯器會把兩邊都當作參數，屬性永遠不會被賦值。

```c++
#include <iostream>

using namespace std;

class Student {
public:
    string name;
    int age;

    void setName(string name) {
        //name = name;        // 自己賦值給自己，成員變數沒有被改到
        this->name = name;     // 左邊是成員變數，右邊是參數
    }
    void setAge(int age) {
        //age = age;          // 自己賦值給自己，成員變數沒有被改到
        this->age = age;       // 同上
    }

    void printInfo() {
        cout << "Name: " << name << ", Age: " << age << endl;
    }
};


int main()
{
    Student std;
    std.setName("Alice");
    std.setAge(20);

    std.printInfo();

    return 0;
}

```

使用 this
<div class="coutput">
Name: Alice, Age: 20
</div>

不使用 this
<div class="coutput">
Name: , Age: -103249728
</div>


<link rel=stylesheet type="text/css" href="https://nlmoodle.ddns.net/css/h.css?v=20240325001">

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

## 一、class 裡的東西，不是誰都可以動的

先看一個銀行帳戶的例子。

```c++
#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:` 將存取限制改為「私有的」看看差別。

```c++
#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 開放幾個成員函數，給外界有限能力操作內部屬性

```c++
#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 存取修飾詞的作用範圍

```c++
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，包含了很多的屬性和成員函數，但是對外界來說，它被一個「盒子」封裝起來，你只操作這個盒子外漏的那幾個界面。

<link rel=stylesheet type="text/css" href="https://nlmoodle.ddns.net/css/h.css?v=20240325001">

# 10-3 如何建立複雜的類別

## 一、成員函數可以定義在 class 之外

我們可以只在 class body 中 `宣告` member function，再將它 `定義` 在 class body 之外。

```c++
class sprite {
private:
    string name;
public:
    sprite(string _name);  // 只有宣告
    string getName();      // 只有宣告
};

// 定義 sprite 類別的 sprite 成員函數
sprite::sprite(string _name)
{
    name = _name;
}

// 定義 sprite 類別的 getName 成員函數
string sprite::getName()
{
    return name;
}
```

以下是一個遊戲中角色的範例

```c++
#include <iostream>

using namespace std;

class sprite
{
private:
    string name;
    string status;
    int HP;
    int maxHP;

    void setStatus(void);  // 根據 hp 設定健康狀態
    void drawHpBar(void);  // 繪製 hp 長條圖
public:
    sprite(string _name);
    string getName();      // 取得姓名
    void decHP(int n);     // 減少 HP
    void addHP(int n);     // 增加 HP
    void show(void);       // 顯示狀態
};

void sprite::setStatus(void)
{
    if(HP >= maxHP *0.9)
        status = "健康";
    else if(HP >= maxHP *0.6)
        status = "受傷";
    else if(HP >= maxHP *0.3)
        status = "重傷";
    else if(HP > 0)
        status = "瀕死";
    else
        status = "死亡";
}

sprite::sprite(string _name)
{
    name = _name;
    HP = maxHP = 20;
    status = "健康";
}

string sprite::getName()
{
    return name;
}

void sprite::decHP(int n)
{
    HP = HP - n;
    if(HP <0)
        HP = 0;
    setStatus();
}

void sprite::addHP(int n)
{
    HP = HP + n;
    if(HP > maxHP)
        HP = maxHP;
    setStatus();
}

void sprite::drawHpBar(void)
{
    int a = 10*HP/maxHP;
    cout << "[";
    for(int i=0; i<a; i++)
        cout << "#";
    for(int i=a; i<10; i++)
        cout << "-";
     cout << "] ";
}

void sprite::show(void)
{
    cout << "[" << name << "] ";
    cout << "狀態：" << status << endl;
    drawHpBar();
    cout << "HP: " << HP  << "/" << maxHP << endl;
    cout << endl;
}

void attack(sprite &s, int n)
{
    cout << s.getName() << " 受到 " << n << " 點的傷害。" << endl;
    cout << endl;
    s.decHP(n);
}


int main()
{
    sprite fighter1("David");
    fighter1.show();
    attack(fighter1, 6);
    fighter1.show();
    attack(fighter1, 6);
    fighter1.show();
    attack(fighter1, 6);
    fighter1.show();
    attack(fighter1, 6);
    fighter1.show();

    return 0;
}
```
<div class="coutput">
David 受到 6 點的傷害。

[David] 狀態：受傷
[#######---] HP: 14/20

David 受到 6 點的傷害。

[David] 狀態：重傷
[####------] HP: 8/20

David 受到 6 點的傷害。

[David] 狀態：瀕死
[#---------] HP: 2/20

David 受到 6 點的傷害。

[David] 狀態：死亡
[----------] HP: 0/20
</div>

## 二、將類別放在獨立的檔案中

在前一個例子裡，我們把 sprite 類別和 main 放在同一個檔案裡，在程式碼短的情形下是沒問題的。但在動輒使用到數十乃至上百個類別的程式裡，這麼做就不切實際了，會造成維護和分工上的困擾。

我們可以將sprite類別移到獨立的檔案裡，這樣修改 sprite 時就可以專注在 sprite 的程式碼，main.cpp 裡也不會顯得凌亂，建置專案時的效率也會更好。

### sprite.h

```c++
#ifndef SPRITE_H_INCLUDED
#define SPRITE_H_INCLUDED

#include <iostream>

using namespace std;

class sprite
{
private:
    string name;
    string status;
    int hp;
    int maxHp;

    void setStatus(void);  // 根據 hp 設定健康狀態
    void drawHpBar(void);  // 繪製 hp 長條圖
public:
    sprite(string _name);
    string getName();      // 取得姓名
    void decHP(int n);     // 減少 HP
    void addHP(int n);     // 增加 HP
    void show(void);       // 顯示狀態
};

#endif // SPRITE_H_INCLUDED
```

### sprite.cpp

```c++
#include "sprite.h"

void sprite::setStatus(void)
{
    if(HP >= maxHP*0.9)
        status = "健康";
    else if(HP >= maxHP*0.6)
        status = "受傷";
    else if(HP >= maxHP*0.3)
        status = "重傷";
    else if(HP > 0)
        status = "瀕死";
    else
        status = "死亡";
}

sprite::sprite(string _name)
{
    name = _name;
    HP = maxHP = 20;
    status = "健康";
}

string sprite::getName()
{
    return name;
}

void sprite::decHP(int n)
{
    HP = HP - n;
    if(HP<0)
        HP = 0;
    setStatus();
}

void sprite::addHP(int n)
{
    HP = HP + n;
    if(HP > maxHP)
        HP = maxHP;
    setStatus();
}

void sprite::drawHpBar(void)
{
    int a = 10*HP/maxHP;
    cout << "[";
    for(int i=0; i<a; i++)
        cout << "#";
    for(int i=a; i<10; i++)
        cout << "-";
     cout << "] ";
}

void sprite::show(void)
{
    cout << "[" << name << "] ";
    cout << "狀態：" << status << endl;
    drawHpBar();
    cout << "HP: " << HP  << "/" << maxHP << endl;
    cout << endl;
}
```

### main.cpp

```c++
#include <iostream>
#include "sprite.h"

using namespace std;

void attack(sprite &s, int n)
{
    cout << s.getName() << " 受到 " << n << " 點的傷害。" << endl;
    cout << endl;
    s.decHP(n);
}

int main()
{
    sprite fighter1("David");
    fighter1.show();
    attack(fighter1, 6);
    fighter1.show();
    attack(fighter1, 6);
    fighter1.show();
    attack(fighter1, 6);
    fighter1.show();
    attack(fighter1, 6);
    fighter1.show();

    return 0;
}
```

### 建置時的檔案相依性

<div drawio-diagram="146"><img src="https://nlmoodle.ddns.net/uploads/images/drawio/2026-05/drawing-1-1780188695.png"></div>

<link rel=stylesheet type="text/css" href="https://nlmoodle.ddns.net/css/h.css?v=20240325001">

# 10-4 Class 練習題

## 練習一：電影票券（`Ticket`）

### 情境說明

你正在設計一套電影院售票系統。每張票券記錄了電影名稱、座位號碼、票價，以及是否已被使用。票券一旦使用就不能再次入場；票價不能設為負數。

---

### 規格列表

#### 成員變數（皆為 `private`）

| 變數名稱 | 型態 | 說明 |
|---|---|---|
| `movieName` | `string` | 電影名稱 |
| `seatNumber` | `int` | 座位號碼 |
| `price` | `int` | 票價（元） |
| `used` | `bool` | 是否已使用 |

#### 建構子

| 建構子 | 初始值 |
|---|---|
| `Ticket()` | `movieName="未命名"`、`seatNumber=0`、`price=0`、`used=false` |
| `Ticket(string movieName, int seatNumber, int price)` | 依參數設定，`used=false` |

> 兩個建構子的參數名稱與成員變數相同，需使用 `this->` 或初始化列表。

#### Getter（皆加 `const`）

| 函式 | 回傳型態 | 說明 |
|---|---|---|
| `getMovieName()` | `string` | 回傳電影名稱 |
| `getSeatNumber()` | `int` | 回傳座位號碼 |
| `getPrice()` | `int` | 回傳票價 |
| `isUsed()` | `bool` | 回傳是否已使用 |

#### Setter（含驗證）

| 函式 | 驗證規則 |
|---|---|
| `setPrice(int price)` | `price < 0` 時印出 `"票價不能為負數！"` 並放棄修改 |

#### 其他成員函式

| 函式 | 回傳 | 說明 |
|---|---|---|
| `use()` | `bool` | 若已使用，印出 `"此票券已使用過！"` 並回傳 `false`；否則將 `used` 設為 `true`，印出入場訊息並回傳 `true` |
| `print() const` | `void` | 印出票券所有資訊（格式見預期輸出） |

---

### `main()`

```cpp
int main() {
    Ticket t1("星際大戰", 12, 280);
    Ticket t2;

    t1.print();
    t2.print();

    t1.use();
    t1.use();           // 重複使用

    t1.setPrice(-100);  // 非法
    t1.setPrice(250);   // 合法

    cout << t1.getMovieName() << " 目前票價：" << t1.getPrice() << " 元" << endl;

    t1.print();
}
```

### 預期輸出

<div class="coutput">
[票券] 星際大戰 | 座位 12 | 票價 280 元 | 狀態：未使用
[票券] 未命名 | 座位 0 | 票價 0 元 | 狀態：未使用
入場成功！電影：星際大戰，座位：12
此票券已使用過！
票價不能為負數！
星際大戰 目前票價：250 元
[票券] 星際大戰 | 座位 12 | 票價 250 元 | 狀態：已使用
</div>

---

## 練習二：水壺（`Pitcher`）

### 情境說明

你設計一個登山水壺管理程式。每個水壺有名稱、最大容量和目前水量。注水時若超過容量會溢出；倒水時若水量不足則失敗；容量不能設得比目前水量還小。

---

### 規格列表

#### 成員變數（皆為 `private`）

| 變數名稱 | 型態 | 說明 |
|---|---|---|
| `name` | `string` | 水壺名稱 |
| `capacity` | `int` | 最大容量（ml） |
| `current` | `int` | 目前水量（ml） |

#### 建構子

| 建構子 | 初始值 |
|---|---|
| `Pitcher()` | `name="水壺"`、`capacity=1000`、`current=0` |
| `Pitcher(string name, int capacity)` | 依參數設定，`current=0` |

#### Getter（皆加 `const`）

| 函式 | 回傳型態 | 說明 |
|---|---|---|
| `getName()` | `string` | 回傳水壺名稱 |
| `getCapacity()` | `int` | 回傳最大容量 |
| `getCurrent()` | `int` | 回傳目前水量 |

#### Setter（含驗證）

| 函式 | 驗證規則 |
|---|---|
| `setCapacity(int capacity)` | `≤ 0` 印出 `"容量必須大於 0！"`；新容量 `< current` 印出 `"新容量小於目前水量，無法設定！"` |

#### 其他成員函式

| 函式 | 回傳 | 說明 |
|---|---|---|
| `fill(int amount)` | `void` | 注水；若超出容量則補滿並印出溢出量，否則正常注水 |
| `pour(int amount)` | `bool` | 倒水；水量不足回傳 `false` 並印出提示，否則正常倒出回傳 `true` |
| `status() const` | `void` | 印出水壺狀態，含百分比（格式見預期輸出） |

---

### `main()`

```cpp
int main() {
    Pitcher p1("運動水壺", 750);
    Pitcher p2;

    p1.status();
    p2.status();

    p1.fill(500);
    p1.fill(400);       // 會溢出

    p1.pour(200);
    p1.pour(600);       // 水量不足

    p1.setCapacity(-1); // 非法
    p1.setCapacity(100);// 非法（小於目前水量 550）

    cout << p1.getName() << " 容量：" << p1.getCapacity() << " ml" << endl;

    p1.status();
}
```

### 預期輸出

<div class="coutput">
[運動水壺] 0 / 750 ml（0%）
[水壺] 0 / 1000 ml（0%）
運動水壺 注入 500 ml。
運動水壺 已裝滿，多餘 150 ml 溢出。
運動水壺 倒出 200 ml。
運動水壺 水量不足，無法倒出 600 ml！
容量必須大於 0！
新容量小於目前水量，無法設定！
運動水壺 容量：750 ml
[運動水壺] 550 / 750 ml（73%）
</div>

---

## 練習三：遊戲計分板（`ScoreBoard`）

### 情境說明

你正在設計一款小遊戲的計分系統。計分板記錄玩家名稱、歷史最高分、當前分數和剩餘生命數。每次得分若超過歷史最高分則更新紀錄；失去所有生命時宣告遊戲結束；重新開始時分數歸零但最高分保留。

---

### 規格列表

#### 成員變數（皆為 `private`）

| 變數名稱 | 型態 | 說明 |
|---|---|---|
| `playerName` | `string` | 玩家名稱 |
| `highScore` | `int` | 歷史最高分 |
| `currentScore` | `int` | 當前分數 |
| `lives` | `int` | 剩餘生命數 |

#### 建構子

| 建構子 | 初始值 |
|---|---|
| `ScoreBoard()` | `playerName="玩家"`、`highScore=0`、`currentScore=0`、`lives=3` |
| `ScoreBoard(string playerName, int lives)` | 依參數設定，`highScore=0`、`currentScore=0` |

#### Getter（皆加 `const`）

| 函式 | 回傳型態 | 說明 |
|---|---|---|
| `getPlayerName()` | `string` | 回傳玩家名稱 |
| `getHighScore()` | `int` | 回傳歷史最高分 |
| `getCurrentScore()` | `int` | 回傳當前分數 |
| `getLives()` | `int` | 回傳剩餘生命數 |

#### Setter（含驗證）

| 函式 | 驗證規則 |
|---|---|
| `setPlayerName(string playerName)` | 名稱為空字串時印出 `"玩家名稱不能為空！"` 並放棄修改 |

#### 其他成員函式

| 函式 | 回傳 | 說明 |
|---|---|---|
| `addScore(int points)` | `void` | 加分（`≤0` 印出提示拒絕）；若 `currentScore > highScore` 則更新並印出新紀錄訊息 |
| `loseLife()` | `bool` | 扣一條命；`lives=0` 時印出遊戲結束訊息並回傳 `false`，否則回傳 `true` |
| `reset()` | `void` | `currentScore` 歸零、`lives` 回復 3，`highScore` 保留，印出提示 |
| `print() const` | `void` | 印出所有資訊（格式見預期輸出） |

---

### `main()`

```cpp
int main() {
    ScoreBoard sb("小明", 3);
    ScoreBoard sb2;

    sb.print();
    sb2.print();

    sb.addScore(100);
    sb.addScore(250);
    sb.addScore(50);

    sb.loseLife();
    sb.loseLife();
    sb.loseLife();      // 第三條命，遊戲結束

    sb.setPlayerName(""); // 非法
    sb.setPlayerName("小明Pro");

    sb.reset();
    sb.print();

    sb.addScore(500);   // 超越舊最高分
    sb.print();
}
```

### 預期輸出

<div class="coutput">
== 小明 == 當前：0 | 最高：0 | 命：3
== 玩家 == 當前：0 | 最高：0 | 命：3
新紀錄！最高分更新為 100
小明 得 100 分，目前：100
新紀錄！最高分更新為 350
小明 得 250 分，目前：350
新紀錄！最高分更新為 400
小明 得 50 分，目前：400
小明 失去一條命，剩餘：2
小明 失去一條命，剩餘：1
小明 失去一條命，剩餘：0
遊戲結束！小明 最終得分：400
玩家名稱不能為空！
小明Pro 重新開始，最高分保留：400
== 小明Pro == 當前：0 | 最高：400 | 命：3
新紀錄！最高分更新為 500
小明Pro 得 500 分，目前：500
== 小明Pro == 當前：500 | 最高：500 | 命：3
</div>


<link rel=stylesheet type="text/css" href="https://nlmoodle.ddns.net/css/h.css?v=20240325001">