之前就有學過物件導向程式設計(OOP)的「觀念」,但一直沒有認真看相關的程式碼,現在就來透過 C++ 程式碼,搞懂 OOP 是怎麼實現的。
class 與實例 instance「類別」是用來建立物件的模板,而從模板做出來的一個個新物件稱為「實例」。
以下列範例來說,main() 函式裡面以 Flashlight 類別為基礎,建立一個名為 myLight 的實例:
#include <iostream>
// Flashlight 類別
class Flashlight {
public:
// 屬性(資料)
bool isOn;
// 方法(函數)
void toggle() {
isOn = !isOn;
if (isOn) {
std::cout << "Flashlight on\n";
} else {
std::cout << "Flashlight off\n";
}
}
};
int main() {
// 以 Flashlight 類別為模板做的實例,命名為 myLight
Flashlight myLight;
myLight.isOn = false; // 初始化 myLight 實例的屬性
myLight.toggle(); // 呼叫 myLight 實例的方法
}
當一個類別的物件被建立時,會自動呼叫「建構子(constructor)」函數,其特色包含:
而當一類別的物件被銷毀時,會自動呼叫「解構子(destructor)」函數,其特色包含:
~ 開頭,函數名稱也跟類別一樣,亦無回傳型別且沒有參數。值得留意的是,如果直接宣告多於一個實例,這些物件實例會存在記憶體的「堆疊(stack)」中,而堆疊有「後進先出」特性,因此後建立的物件反而會先死亡。
上述說明可搭配下列程式碼觀察,當中的 private 跟 public 會在下個段落說明:
#include <iostream>
#include <string>
using namespace std;
class Player {
private: // 下個段落說明 private 意義
string name; // Player 類別的角色名字
public: // 下個段落說明 public 意義
// 建構子:名字和類別一樣都是 Player,負責在物件誕生時把名字設定好
Player(string inputName) {
name = inputName;
cout << name << " is born!" << endl;
}
// 建構子的另一種更好的寫法:成員初始化列表(member initializer list)
// 確保資料誕生就完成初始化
// 而非先建立預設值後再賦值覆蓋
// Player(string inputName) : name(inputName) {
// // 大括號內只需專心處理實際邏輯,不用管資料建立過程
// cout << name << " is born!" << endl;
// }
// 解構子:名字和類別一樣都是 Player,但前面加上 ~,負責在物件死亡時收尾
~Player() {
cout << name << " is dead..." << endl;
}
};
int main() {
{ // 大括號內為作用域域範圍(Scope)
Player p1("Arthur"); // 第一個物件 p1 誕生,自動執行建構子
Player p2("Anna"); // 第二個物件 p2 誕生,自動執行建構子
} // 離開大括號,p2 物件的生命週期先結束,自動執行解構子
// 接著, p1 物件的生命週期也結束,自動執行解構子
}
執行結果如下,可發現後建立的實例(名為 Anna 的 p2)反而先死亡:
Arthur is born!
Anna is born!
Anna is dead...
Arthur is dead...
「封裝」共有兩個要點:
public 與 private不論屬性或方法,只要寫在 public 區就表示在此類別外也可以取用、private 則表示僅限於同類別中使用。
以下列範例而言,只有類別內部可以動到 balance 屬性:
#include <iostream>
class BankAccount {
private:
int balance = 0; // 只有 BankAccount 這個類別的內部函數可以存取 balance 變數
public:
// 非 BankAccount 類別也可以操作 deposit() 函數存款
// deposit() 函數也在同一類別中,因此可操控 balance
void deposit(int amount) {
if (amount > 0) {
balance += amount;
std::cout << "Deposit " << amount << " Made\n";
} else {
std::cout << "Balance should be at least 0\n";
}
}
// 非 BankAccount 類別也可以操作 getBalance() 函數查詢餘額
int getBalance() {
return balance;
}
};
int main() {
BankAccount myAccount; // 以 BankAccount 類別建立一個實例,實例名為 myAccount
// main() 函數中不能直接存取 private 區塊的 balance 屬性
// myAccount.balance = -1000;
// main() 函數中可以存取 public 區塊的 deposit() 函數
// deposit() 函數再默默調整 balance 的值
myAccount.deposit(500); // 成功存款:Deposit 500 Made
myAccount.deposit(-100); // 錯誤:Balance should be at least 0
// main() 函數中可以存取 public 的 getBalance() 函數
// 顯示 Current balance: 500 dollars
std::cout << "Current balance: " << myAccount.getBalance() << " dollars\n";
}
執行結果如下:
Deposit 500 Made
Balance should be at least 0
Current balance: 500 dollars
剛剛的範例是以「銀行帳戶 BankAccount」為類別,現在要新增兩類別:
FixedDepositAccount」SecuritiesAccount」這兩種帳戶也都要有 balance 變數跟 getBalance() 方法,但建立兩新類別時若都各自再定義 balance 跟 getBalance() 顯得累贅,因此可以從 BankAccount 類別延伸而來。
首先,定存帳戶類別可以這樣定義:
class FixedDepositAccount : public BankAccount { // 表示 FixedDepositAccount 類別繼承自 BankAccount 類別
public:
void applyMonthlyInterest() { // FixedDepositAccount 新類別自有方法
int interest = balance * 0.05;
balance += interest; // 思考:可以直接操作 balance 變數嗎?
std::cout << "[Fixed Deposit] Monthly interest of " << interest << " dollars has been credited.\n";
}
};
證券帳戶類別可以這樣定義:
class SecuritiesAccount : public BankAccount { // 表示 SecuritiesAccount 類別繼承自 BankAccount 類別
public:
void buyStock(std::string stockCode, int cost) { // SecuritiesAccount 新類別自有方法
if (balance >= cost) {
balance -= cost; // 思考:可以直接操作 balance 變數嗎?
std::cout << "[Securities] Successfully purchased stock " << stockCode << ", deducted " << cost << " dollars.\n";
} else {
std::cout << "[Securities] Insufficient balance, unable to purchase stock!\n";
}
}
};
protected 區塊在這兩個新類別中,都有直接操作 balance 變數,但當初為了不讓 balance 被任意改動而被放在 private 區,這樣這些新類別還能動 balance 嗎?
要讓變數或方法不輕易被其他地方改動,但又希望可以被繼承下來的新類別改動,還有另外一種稱為 protected 的區塊,因此可以把原本的 BankAccount 類別改寫如下:
class BankAccount {
protected:
int balance = 0; // 從 private 搬到 protected,讓繼承的新類別可以動、其他地方還是不能動
public:
void deposit(int amount) {
if (amount > 0) {
balance += amount;
std::cout << "Deposit " << amount << " Made\n";
} else {
std::cout << "Balance should be at least 0\n";
}
}
int getBalance() {
return balance;
}
};
假設又有一個名為 WealthManagementAccount 的新類別要同時繼承剛剛的 FixedDepositAccount 跟 SecuritiesAccount,表示這個帳戶同時可以定存又可以買賣股票:
class WealthManagementAccount : public FixedDepositAccount, public SecuritiesAccount {
// 同時擁有 applyMonthlyInterest() 和 buyStock() 方法
};
新類別同時繼承兩個舊類別是合法的,但問題在於這兩個中間類別又都繼承自同一個更上層的 BankAccount 類別,而 balance 變數就放在 BankAccount 類別中:
[ BankAccount (擁有 balance) ]
/ \
[ FixedDepositAccount ] [ SecuritiesAccount ]
\ /
[ WealthManagementAccount ]
在 FixedDepositAccount 跟 SecuritiesAccount 兩類別的 balance 都是從 BankAccount 類別拷貝而來的。
這會造成如果要在 WealthManagementAccount 類別對 balance 進行操作,編譯器會不清楚到底要拿 FixedDepositAccount 那邊的 balance,還是 SecuritiesAccount 那邊的 balance,這就是「菱形問題(diamond problem)」。
[ BankAccount ]
balance 變數
/ \
[ FixedDepositAccount ] [ SecuritiesAccount ]
balance 變數拷貝一 balance 變數拷貝二
\ /
[ WealthManagementAccount ]
不知道 balance 要取拷貝一還是拷貝二
要解決菱形問題,可以用「虛擬繼承」,其核心觀念為讓所有新類別都共同指向同一塊屬於 BankAccount 類別 balance 變數的記憶體空間:
[ BankAccount (擁有 balance) ]
balance 變數
/ ^^^ \
[ FixedDepositAccount ] ||| [ SecuritiesAccount ]
指向 balance 變數指標 -─┘|└─- 指向 balance 變數指標
\ | /
指向 balance 變數指標
[ WealthManagementAccount ]
而寫法則是變成:
#include <iostream>
#include <string>
// // 最頂層:基礎類別
class BankAccount {
protected:
int balance = 0; // 下方虛擬繼承用的 balance 變數都是這個
public:
void deposit(int amount) {
if (amount > 0) {
balance += amount;
std::cout << "Deposit " << amount << " Made\n";
}
}
int getBalance() {
return balance;
}
};
// 中間層:加上 virtual 關鍵字
// 虛擬繼承:balance 變數也來自最頂層的 BankAccount 類別
class FixedDepositAccount : virtual public BankAccount {
public:
void applyMonthlyInterest() {
int interest = balance * 0.05;
balance += interest;
std::cout << "[Fixed Deposit] Monthly interest of " << interest << " dollars has been credited.\n";
}
};
// 虛擬繼承:balance 變數也來自最頂層的 BankAccount 類別
class SecuritiesAccount : virtual public BankAccount {
public:
void buyStock(std::string stockCode, int cost) {
if (balance >= cost) {
balance -= cost;
std::cout << "[Securities] Successfully purchased stock " << stockCode << ", deducted " << cost << " dollars.\n";
} else {
std::cout << "Balance should be at least 0\n";
}
}
};
// 最底層:balance 變數也來自最頂層的 BankAccount 類別,但不用寫 virtual
class WealthManagementAccount : public FixedDepositAccount, public SecuritiesAccount {
public:
// 這裡的 balance 也會是最頂層 BankAccount 類別中的 balance 變數
};
多型比較複雜一些,表示讓不同物件針對同樣的訊息有差異化的操作,包含:
表示可以有同名、但參數的數量或種類不同。
以 BankAccount 類別而言,假設有兩種存款方式,一是存入現金、二是存入支票,就可以定義兩個函數,但名字皆為 deposit:
class BankAccount {
public:
// 版本 A:存入現金,參數為 int 型別
void deposit(int amount) {
if (amount > 0) {
balance += amount;
std::cout << "[Overload A] Cash Deposit of $" << amount << " Made.\n";
}
}
// 版本 B:存入支票(字串)。函數同名,但參數變成 string
void deposit(std::string checkNumber) {
// 假設支票一律兌現 $10000
balance += 10000;
std::cout << "[Overload B] Check #" << checkNumber << " Processed. $10000 Deposited.\n";
}
};
當 main() 函數以 BankAccount 類別開了一個名為 myAccount 的實例:
BankAccount myAccount;
那如果遇到 deposit() 函數時:
myAccount.deposit(500); // 傳入整數型別,跑版本 A
myAccount.deposit("CH109238"); // 傳入字串型別,跑版本 B
在編譯時期,編譯器就會自動根據參數狀況判斷要跑哪種版本。
可定義如 +、-、* 等運算子如何在定義的物件上使用。
例如在 BankAccount 類別中,定義 + 運算子可讓兩個 BankAccount 類別相加:
BankAccount operator+(const BankAccount& other) {
BankAccount combinedAccount;
combinedAccount.balance = this->balance + other.balance;
return combinedAccount;
}
首先第一行:
BankAccount operator+(const BankAccount& other)
分別表示:
BankAccount:表示算完之後會回傳一個 BankAccount 物件。operator+:表示現在要定義 + 這個運算子的行為。(const BankAccount& other):參數名稱為 other、型別為 BankAccount 且用參考傳遞而非傳整個物件,前面加上 const 保證不會改到傳進來的 other。再來的:
BankAccount combinedAccount;
表示宣告一個型別為 BankAccount 的變數 combinedAccount。
combinedAccount.balance = this->balance + other.balance;
則是說 combinedAccount 的 balance 變數會是目前這個物件自己的餘額 this->balance 跟傳進來的 other 物件的餘額 other.balance 相加。
在 main() 函數中,以 BankAccount 為模板創建兩個實例,實例名稱分別為 acc1、acc2,先取用這兩個實例的 deposit() 方法分別存入 300 元、200 元:
BankAccount acc1; // 第一個實例
BankAccount acc2; // 第二個實例
acc1.deposit(300); // 第一個實例存 300
acc2.deposit(200); // 第二個實例存 200
接著神奇的事情發生了,acc1 跟 acc2 兩個物件居然能夠直接相加,相加後的結果賦值於名為 mergedAcc 的新實例:
BankAccount mergedAcc = acc1 + acc2;
透過新的 mergedAcc 物件 getBalance() 方法就能得到相加後的餘額:
std::cout << "Merged Account Balance: $" << mergedAcc.getBalance() << "\n";
如果沒有運算子重載而想要達到一樣效果,那就必須寫成:
BankAccount mergedAcc = combineAccounts(acc1, acc2);
這個 combineAccounts 函數也要另外定義:
BankAccount combineAccounts(const BankAccount& a, const BankAccount& b) {
BankAccount result;
result.setBalance(a.getBalance() + b.getBalance());
return result;
}
函數覆寫的多型是動態的,表示要等程式已經跑起來、真正執行到那一行時,才決定當下怎麼做。
假設要印出某位客戶所有類別帳戶的餘額,可是每種帳戶顯示餘額的方式不一樣,就可以在原本的上層類別先宣告帶有 virtual 的函數 printReport():
virtual void printReport() {
std::cout << "[Standard Account] Current Balance: $" << balance << "\n";
}
在新類別中,再各自覆寫 printReport(),如定存帳戶中:
class FixedDepositAccount : public BankAccount {
private:
double interestRate = 0.05; // 定存專戶才有的利息
public:
// 覆寫上層類別的 printReport() 方法
void printReport() override {
std::cout << "[Fixed Deposit Account] Balance: $" << balance
<< " (Estimated Annual Interest: $" << balance * interestRate << ")\n";
}
};
還有證券帳戶的:
class SecuritiesAccount : public BankAccount {
private:
int stockCount = 10; // 證券帳戶專有,假設持有某股票 10 股
public:
// 覆寫上層類別的 printReport() 方法
void printReport() override {
std::cout << "[Securities Account] Balance: $" << balance
<< " (Active Investments: " << stockCount << " shares)\n";
}
};
在 main() 函數中,先定義好不同的實例:
BankAccount regular; // 一般帳戶實例
FixedDepositAccount fxAcc; // 定存帳戶實例
SecuritiesAccount secAcc; // 證券帳戶實例
先給這些帳戶各自存一些金額:
regular.deposit(1000); // 一般帳戶存 1000
fxAcc.deposit(5000); // 定存帳戶存 5000
secAcc.deposit(3000); // 證券帳戶存 3000
再來建立一個名為 bankSystem 的陣列,陣列的每個元素都是指向 BankAccount 物件的指標(也就是 BankAccount 物件的記憶體位址):
std::vector<BankAccount*> bankSystem;
再來依序把三個記憶體位址的指標都放到 bankSystem 陣列中:
bankSystem.push_back(®ular);
bankSystem.push_back(&fxAcc);
bankSystem.push_back(&secAcc);
雖然實際放進去的實例來自 BankAccount、FixedDepositAccount 與 SecuritiesAccount 三種不同類別,但因為 FixedDepositAccount 跟 SecuritiesAccount 都是 BankAccount 類別的小孩,所以它們的指標都會經過「向上轉型(upcasting)」,被當成上層類別指標 BankAccount*,因此也可以放進 bankSystem 陣列中。
接著進到本段落最重要的核心:
for (BankAccount* acc : bankSystem) {
acc->printReport();
}
這段程式碼表示在 bankSystem 這個陣列中,對於每一個名為 acc 的「指向 BankAccount 的指標」而言,都去做當中的 printReport() 方法。
因此:
regular 實例執行的是寫在 BankAccount 類別的printReport()。fxAcc 實例執行的是寫在 FixedDepositAccount 類別的 printReport()。secAcc 實例執行的是寫在 SecuritiesAccount 類別的 printReport()。在程式編譯完成後的執行過程中,電腦會自己去辨認要取哪一個類別中定義的方法。
+、- 等運算子重定義,讓物件也可套用。virtual、下層類別用 override 覆寫;下層類別指標可「向上轉型」,使上下層類別指標放在同一陣列。三者綜合範例如下:
#include <iostream>
#include <string>
#include <vector>
class BankAccount {
protected:
int balance = 0;
public:
// ========= 函數多載 =========
// 版本 A:存入現金(整數)
void deposit(int amount) {
if (amount > 0) {
balance += amount;
std::cout << "[Deposit A] Cash Deposit of $" << amount << " Made.\n";
}
}
// 版本 B:存入支票(字串)
void deposit(std::string checkNumber) {
balance += 10000; // 假設如果存支票就存 $10000
std::cout << "[Deposit B] Check #" << checkNumber << " Processed. $10000 Deposited.\n";
}
// ========= 運算子重載 =========
BankAccount operator+(const BankAccount& other) {
BankAccount combinedAccount;
combinedAccount.balance = this->balance + other.balance;
return combinedAccount;
}
// ========= 函數覆寫(上層類別) =========
virtual void printReport() {
std::cout << "[Standard Account] Current Balance: $" << balance << "\n";
}
int getBalance() { return balance; }
// 因為此類別內有帶 virtual 的函數,解構子也必須要有 virtual
// 才能確保未來若透過上層類別指標銷毀物件時,下層類別專有的記憶體內容也會完整被銷毀
// 避免記憶體洩漏
virtual ~BankAccount() {}
};
// ========= 下層類別 1:定存專戶 =========
class FixedDepositAccount : public BankAccount {
private:
double interestRate = 0.05; // 假設利率為 0.05
public:
// 函數覆寫:覆寫上層類別的 printReport() 方法
void printReport() override {
std::cout << "[Fixed Deposit Account] Balance: $" << balance
<< " (Estimated Annual Interest: $" << balance * interestRate << ")\n";
}
};
// ========= 下層類別 2:證券專戶 =========
class SecuritiesAccount : public BankAccount {
private:
int stockCount = 10; // 假設持有某股票 10 股
public:
// 函數覆寫:覆寫上層類別的 printReport() 方法
void printReport() override {
std::cout << "[Securities Account] Balance: $" << balance
<< " (Active Investments: " << stockCount << " shares)\n";
}
};
// ========= 主程式測試 =========
int main() {
BankAccount myAccount;
std::cout << "=== Testing Function Overload ===\n";
myAccount.deposit(500); // 函數多載版本 A (現金)
myAccount.deposit("CH109238"); // 函數多載版本 B (支票)
myAccount.printReport(); // 目前餘額為 500 + 10000 = 10500
std::cout << "\n=== Testing Operator Overload ===\n";
// 用 BankAccount 類別建立兩實例
BankAccount acc1;
BankAccount acc2;
// 兩實例再各自取其 deposit() 函數存款
acc1.deposit(300);
acc2.deposit(200);
// 運算子重載:直接用 + 號把兩個物件實例相加
BankAccount mergedAcc = acc1 + acc2;
std::cout << "Merged Account Balance: $" << mergedAcc.getBalance() << "\n"; // 輸出 500
std::cout << "\n=== Testing Dynamic Polymorphism & Function Override ===\n";
// 建立三種不同的帳戶實例
BankAccount regular; // 以上層類別 BankAccount 為模板
FixedDepositAccount fxAcc; // 以下層類別 FixedDepositAccount 為模板
SecuritiesAccount secAcc; // 以下層類別 SecuritiesAccount 為模板
// 為三個帳戶實例用 deposit() 函數存入初始金額
regular.deposit(1000);
fxAcc.deposit(5000);
secAcc.deposit(3000);
// 用上層類別的指標,把三種帳戶物件的位址統統裝進 bankSystem 陣列
std::vector<BankAccount*> bankSystem;
bankSystem.push_back(®ular); // 上層類別指標
bankSystem.push_back(&fxAcc); // 下層類別物件位址透過「向上轉型」放入陣列
bankSystem.push_back(&secAcc); // 下層類別物件位址透過「向上轉型」放入陣列
std::cout << "\n--- Generating Monthly Reports via Pointer Vector ---\n";
// 執行時期動態判斷,各自執行對應的覆寫版本
for (BankAccount* acc : bankSystem) {
acc->printReport();
}
return 0;
}
執行結果如下:
=== Testing Function Overload ===
[Deposit A] Cash Deposit of $500 Made.
[Deposit B] Check #CH109238 Processed. $10000 Deposited.
[Standard Account] Current Balance: $10500
=== Testing Operator Overload ===
[Deposit A] Cash Deposit of $300 Made.
[Deposit A] Cash Deposit of $200 Made.
Merged Account Balance: $500
=== Testing Dynamic Polymorphism & Function Override ===
[Deposit A] Cash Deposit of $1000 Made.
[Deposit A] Cash Deposit of $5000 Made.
[Deposit A] Cash Deposit of $3000 Made.
--- Generating Monthly Reports via Pointer Vector ---
[Standard Account] Current Balance: $1000
[Fixed Deposit Account] Balance: $5000 (Estimated Annual Interest: $250)
[Securities Account] Balance: $3000 (Active Investments: 10 shares)
指的是「只在乎要做什麼,但不在乎怎麼做」,例如 main() 函數中:
for (BankAccount* acc : bankSystem) {
acc->printReport(); // 只在意要做 printReport(),不在意怎麼做
}
這段迴圈所在的 main() 函數只在意要做 printReport() 這件事,但一般銀行帳戶、定存帳戶跟證券帳戶分別是怎麼實現的,就留給各自的類別去處理。
目前上層類別 BankAccount 仍然有寫出執行細節:
virtual void printReport() {
std::cout << "[Standard Account] Current Balance: $" << balance << "\n";
}
這樣其實還不夠純粹,這函數可以進一步改成在後面加上 = 0 變成「純虛擬函數(pure virtual function)」,表示這個上層類別只是一個純介面,所有繼承此類別的下層類別都要去實作自己專屬的方法:
class BankAccount {
public:
...
virtual void printReport() = 0; // 上層類別只寫 = 0 成為介面,由下層類別寫實作細節
virtual ~BankAccount() {}
};
實作細節留給下層類別處理,例如:
class FixedDepositAccount : public BankAccount {
public:
void printReport() override {
std::cout << "[Fixed Deposit Account] Balance: $" << balance
<< " (Estimated Annual Interest: $" << balance * interestRate << ")\n";
}
};
這樣在 main() 函數中,上層類別 BankAccount 不能被建立成一個實例,只有下層類別可以,且下層類別依然能做向上轉型:
int main() {
// BankAccount acc; // 錯誤:抽象類別不能做成實例
// 下層類別才可以建立實例,且繼承了上層類別的方法
FixedDepositAccount fxAcc;
fxAcc.deposit(5000);
// 下層類別依然可以向上轉型,做多型操作
// 所以可以指向 BankAccount 的指標 acc 也能指向定存帳戶實例的位址
BankAccount* acc = &fxAcc;
// 在 BankAccount 中的 printReport() 發現帶有 virtual
// 所以會去下層定存帳戶類別找帶有 override 的 printReport() 的實作方式
acc->printReport();
}
範本使定義的泛型(generic)函數與類別可不必擔心資料型別。
如過要定義一個比較輸入大小、回傳較大值的函數,可以怎麼寫?
int getMax(int a, int b) {
return (a > b) ? a : b;
}
但這裡的參數都是 int 型別,如果要換成 float 甚至是 string 呢?
// 處理 int 輸入
int getMax(int a, int b) { return (a > b) ? a : b; }
// 處理 double 輸入
double getMax(double a, double b) { return (a > b) ? a : b; }
// 處理 string 輸入
std::string getMax(std::string a, std::string b) { return (a > b) ? a : b; }
明明邏輯都一樣,卻要因為處理型別不同而寫三個函數,那何不寫一個可以通吃所有資料型別的函數?
使用範本 template 就可以實現,範例如下:
#include <iostream>
#include <string>
// 定義一個範本,裡面的 T 代表資料型別尚未決定
template <typename T>
T getMax(T a, T b) { // 不指定型別,由傳入的參數自動推導
return (a > b) ? a : b;
}
int main() {
// 傳入整數,T 自動變成 int
std::cout << getMax(10, 20) << "\n";
// 傳入浮點數,T 自動變成 double
std::cout << getMax(3.14, 2.71) << "\n";
// 傳入字串,T 自動變成 std::string
std::cout << getMax(std::string("apple"), std::string("banana")) << "\n";
}
函數只要寫一次,編譯器會在編譯時會自動判斷型別,執行結果如下:
20
3.14
banana
除了函數,類別也可以先不指定型別,先用 T 暫時頂著,等使用者實際要使用該類別時,再指定資料型別,範例如下:
#include <iostream>
// 建立類別泛型,不指定資料型別
template <typename T>
class Box {
private:
T item; // item 變數不寫死資料型別,以 T 暫代
public:
Box(T inputItem) : item(inputItem) {}
T getItem() {
return item;
}
};
int main() {
// <int> 表示使用者這次指定要用 int 型別,輸入參數為 100
Box<int> numberBox(100);
std::cout << numberBox.getItem() << "\n";
// <std::string> 表示使用者這次指定要用 <std::string> 型別,輸入參數為 "Arthur"
Box<std::string> nameBox("Arthur");
std::cout << nameBox.getItem() << "\n";
}
執行結果如下:
100
Arthur
鋪陳了這麼多 OOP 核心觀念,下一篇就可以用這些內容為基礎,來看 C++ 特殊的「智慧指標」。