繼承本身就具有複雜性,在設計上並不鼓勵,在可以使用其他設計方式替代的場合,例如合成(composite),往往建議別使用繼承;C++ 可以多重繼承,也就是子類別可以同時繼承多個父類,既然單一繼承已經有複雜性了,可想而知地,多重繼承更會急劇地增加複雜度。
限制複雜度的方式之一,是限制只能繼承一個具有狀態定義的父類,因為狀態本身就是複雜的根源,同時繼承多個具有狀態定義的父類,只會令狀態的管理更複雜。
來看看從〈純虛擬函式(二)〉衍生出來的簡單情境,如果今天老闆突發奇想,想把海洋樂園變為海空樂園,有的東西會游泳,有的東西會飛,有的東西會游也會飛,那麼現有的程式可以應付這個需求嗎?
仔細想想,有的東西會飛,但這些東西的狀態定義不一定是相同的,有了〈純虛擬函式(二)〉的經驗,你使用定義了 Flyer
:
class Flyer {
public:
virtual void fly() = 0;
virtual ~Flyer() = default;
};
Flyer
定義了 fly
方法,程式中想要飛的東西,可以繼承 Flyer
,,而有的東西會飛也會遊,例如飛魚,它是一種魚,可以繼承 Fish
,而它也可以飛,因此同時繼承了 Flyer
:
class FlyingFish : public Fish, public Flyer {
public:
using Fish::Fish;
void swim() override {
cout << "飛魚 " + this->name + " 游泳" << endl;
}
void fly() override {
cout << "飛魚 " + this->name + " 飛翔" << endl;
}
};
在這邊運用了多重繼承,若要繼承多個父類,只要用逗號區隔就好了,接著你想,來個超人吧!
class SuperMan : public Flyer, public Swimmer {
protected:
string colorOfunderpants;
public:
SuperMan(string colorOfunderpants) :
colorOfunderpants(colorOfunderpants) {}
string getColorOfunderpants() {
return this->colorOfunderpants;
}
void swim() override {
cout << "超人穿著 " + this->colorOfunderpants + " 內褲在游泳" << endl;
}
void fly() override {
cout << "超人穿著 " + this->colorOfunderpants + " 內褲在飛" << endl;
}
};
雖然叫超人,不過電影裡的超人往往不是人,就不繼承 Human
了,而是繼承 Flyer
與 Swimmer
;接下來,能游的就游,能飛的就飛吧!
...
void doSwim(Swimmer &swimmer) {
swimmer.swim();
}
void doFly(Flyer &flyer) {
flyer.fly();
}
int main() {
Anemonefish anemonefish("尼莫");
Shark shark("蘭尼");
Human human("賈斯汀", "林");
Submarine submarine("黃色一號");
FlyingFish flyingFish("菲爾普斯");
SuperMan superMan("紅色");
doSwim(anemonefish);
doSwim(shark);
doSwim(human);
doSwim(submarine);
doSwim(flyingFish);
doSwim(superMan);
doFly(flyingFish);
doFly(superMan);
return 0;
}
執行結果如下:
小丑魚 尼莫 游泳
鯊魚 蘭尼 游泳
人類 賈斯汀 林 游泳
潛水艇 黃色一號 潛行
飛魚 菲爾普斯 游泳
超人穿著 紅色 內褲在游泳
飛魚 菲爾普斯 飛翔
超人穿著 紅色 內褲在飛
這是多重繼承的一個簡單運用:為了不同狀態定義的類別實例能夠多型。因為繼承的來源沒有狀態定義,只有行為規範,才令多重繼承時的複雜度不致於一下難以控制。