在〈虛擬函式〉中,將 to_string
設成 virtual
了,然而你可能會發現,Role
的子類別都有 fight
方法,為什麼不將它們提昇至父類別並設為 virtual
?可以是可以,不過提昇之後,在 Role
中的方法該寫什麼呢?空的方法本體?如果是這樣的話,不如將它設為純虛擬函式(pure virtual function),也就是沒有任何實作的方法:
class Role {
...略
public:
Role(string name, int level, int blood)
: name(name), level(level), blood(blood) {}
virtual void fight() = 0;
…略
};
這麼一來,就可以如下寫個 doFight
了:
...略
void doFight(Role &role) {
role.fight();
}
int main() {
SwordsMan swordsMan("Justin", 1, 1000);
Magician magician("Magician", 1, 800);
fight(swordsMan);
fight(magician);
return 0;
}
被設為 0 的 virtual
函式,沒有任何實作,是個抽象方法,而擁有抽象方法的類別,是個抽象類別(abstract class),不能用來實例化:
Role role("Justin", 1, 1000); // error: cannot declare variable 'role' to be of abstract type 'Role'
因此,也不能如下指定:
Role role = swordsMan;
也就是說,因為抽象類別不是個完整的類別定義,只用來宣告參考或指標,而繼承抽象類別的子類,一定要重新定義抽象方法,否則該子類也會是抽象類別,無法用來實例化。
來看看抽象方法與抽象類別的另一個應用,如果要你開發一個猜數字遊戲,會隨機產生 0 到 9 的數字,使用者輸入的數字與隨機產生的數字相比,如果相同就顯示「猜中了」,如果不同就繼續讓使用者輸入數字,直到猜中為止。
這程式有什麼難的?相信現在的你也可以實作出來:
#include <iostream>
#include <string>
#include <chrono>
#include <random>
#include <functional>
using namespace std;
function<int()> rand(int from, int to) {
unsigned seed = chrono::system_clock::now()
.time_since_epoch().count(); // 隨機數種子
default_random_engine uniform(seed); // 產生均勻分佈隨機數的引擎
uniform_int_distribution<int> dist(from, to); // 範圍為 0 到 9 均勻分佈
return bind(dist, uniform); // 綁定引擎與範圍
}
int main() {
function<int()> randZeroToNine = rand(0, 9);
int number = randZeroToNine();
int guess = 0;
do {
cout << "輸入數字:";
cin >> guess;
} while(guess != number);
cout << "猜中了!" << endl;
return 0;
}
不過,需求中並沒有說要在文字模式下執行這個遊戲,也就是說取得使用者輸入、顯示結果的環境未定,但你負責的這部份還是要先實作,怎麼辦呢?可以先設計個抽象類別:
class GuessGame {
public:
void go() {
function<int()> randZeroToNine = rand(0, 9);
int number = randZeroToNine();
int guess = 0;
do {
this->print("輸入數字:");
guess = this->nextInt();
} while(guess != number);
println("猜中了!");
}
void println(string text) {
this->print(text + "\n");
}
virtual void print(string text) = 0;
virtual int nextInt() = 0;
virtual ~GuessGame() = default;
};
對於文字模式中的遊戲,可以繼承 GuessGame
:
...略
class ConsoleGame : public GuessGame {
public:
void print(string text) {
cout << text;
}
int nextInt() {
int guess = 0;
cin >> guess;
return guess;
}
};
int main() {
GuessGame &&game = ConsoleGame();
game.go();
return 0;
}
一個執行的結果如下:
輸入數字:3
輸入數字:5
輸入數字:6
輸入數字:7
輸入數字:8
猜中了!