在定義類別時,可以完全只有純虛擬函式,完全不提供實作,也沒有任何狀態定義,將類別當成是一種行為規範。
來個簡單的需求演變情境,以說明為什麼要有這種類別。老闆今天想開發一個海洋樂園遊戲,當中所有東西都會游泳。你想了一下,談到會游的東西,第一個想到的就是魚,你也許會定義 Fish
類別有個 swim
的行為:
class Fish {
protected:
string name;
public:
Fish(string name) : name(name) {}
string getName() {
return this->name;
}
virtual void swim() = 0;
};
由於實際上每種魚游泳方式不同,所以將 swim
定義為純虛擬函式,因此 Fish
是抽象類別。接著定義小丑魚繼承各種魚:
class Fish : public Swimmer {
protected:
string name;
public:
Fish(string name) : name(name) {}
string getName() {
return this->name;
}
};
class Anemonefish : public Fish {
public:
using Fish::Fish;
void swim() override {
cout << "小丑魚 " + this->name + " 游泳" << endl;
}
};
class Shark : public Fish {
public:
using Fish::Fish;
void swim() override {
cout << "鯊魚 " + this->name + " 游泳" << endl;
}
};
class Piranha : public Fish {
public:
using Fish::Fish;
void swim() override {
cout << "食人魚 " + this->name + " 游泳" << endl;
}
};
老闆說話了,為什麼都是魚?人也會游泳啊!怎麼沒寫?於是你就再定義 Human
類別繼承 Fish
…等一下!Human
繼承 Fish
? 不會覺得很奇怪嗎?人是一種魚嗎?既然如此,不如將 Fish
改名為 Swimmer
,讓 Human
繼承 Swimmer
,這樣好像說得過去,魚是一種會游泳的生物,人也是一種會游泳的生物嘛!
不過,如果 Human
要有 firstName
、lastName
兩個值域呢?也就是說就算你將 Fish
改名為 Swimmer
,原本的狀態定義,並不適合 Human
來繼承,怎麼辦呢?
既然都想要抽象的 swim
,而狀態不同是個問題,不如就定義個沒有狀態的 Swimmer
吧!
class Swimmer {
public:
virtual void swim() = 0;
virtual ~Swimmer() = default;
};
然後原本的 Fish
繼承 Swimmer
:
class Fish : public Swimmer {
protected:
string name;
public:
Fish(string name) : name(name) {}
string getName() {
return this->name;
}
};
Fish
方才的子類不用修改,也就是維持既有的繼承體系,接著 Human
也可以繼承 Swimmer
:
class Human : public Swimmer {
protected:
string firstName;
string lastName;
public:
Human(string firstName, string lastName) :
firstName(firstName), lastName(lastName) {}
string getFirstName() {
return this->firstName;
}
string getLastName() {
return this->lastName;
}
void swim() override {
cout << "人類 " + this->firstName + " " + this->lastName + " 游泳" << endl;
}
};
那來個潛水艇吧!
class Submarine : public Swimmer {
protected:
string nickName;
public:
Submarine(string nickName) : nickName(nickName) {}
string getNickName() {
return this->nickName;
}
void swim() override {
cout << "潛水艇 " + this->nickName + " 潛行" << endl;
}
};
現在,大家可以快快樂樂地一起游泳了:
...略
void doSwim(Swimmer &swimmer) {
swimmer.swim();
}
int main() {
Anemonefish anemonefish("尼莫");
Shark shark("蘭尼");
Human human("賈斯汀", "林");
Submarine submarine("黃色一號");
doSwim(anemonefish);
doSwim(shark);
doSwim(human);
doSwim(submarine);
return 0;
}
執行結果如下:
小丑魚 尼莫 游泳
鯊魚 蘭尼 游泳
人類 賈斯汀 游泳
潛水艇 黃色一號 潛行
小丑魚 尼莫 游泳
鯊魚 蘭尼 游泳
人類 賈斯汀 林 游泳
潛水艇 黃色一號 潛行