之前一篇博文我們分析了C++和Java在繼承多态方面的一些簡單比較C/C++繼承類的多态表現,當時我們說到在多态的情況下我們通路的都是父類的行為,那怎樣才能通路到子類的函數呢?答案是通過定義虛函數來實作,那今天我們就寫一個Demo來看一下。
##虛函數##
//測試虛函數
class Animal {
public:
char name[128];
char behavior[128];
//父類的outPut方法設定為虛函數
virtual void outPut() {
cout << "Animal" << endl;
}
void outPutAnimal() {
cout << "Father" << endl;
}
Animal() {
strcpy(name, "Animal");
strcpy(behavior, "Call");
}
virtual ~Animal(){
}
};
class Dog: public Animal {
public:
char name[128];
char sex;
//重寫的方法也是虛函數
void outPut() {
cout << "Dog" << endl;
}
void outPutDog() {
cout << "Son" << endl;
}
Dog() {
strcpy(name, "Dog");
sex = 'M';
}
};
我們來看一下結果
//測試代碼
Animal *dog = new Dog;//多态行為
cout << dog->name << endl;
cout << dog->behavior << endl;
dog->outPut();
dog->outPutAnimal();
//測試結果
Animal
Call
Dog//此刻這裡表現的是子類的行為,如果不是虛函數将表現為父類行為
Father
好啦到此虛函數我們就通過一個示例簡單的分析了一下,但是虛函數在使用的過程中有以下幾個方面的限制。
- 隻有類的成員函數才能是虛函數
- 靜态成員函數不能是虛函數
- 内聯函數不能是虛函數
- 構造函數不能是虛函數,析構函數通常是虛函數
##虛繼承##
我們先來看一個沒有虛繼承的示例
//普通多繼承
class CAnimal //定義一個動物類
{
public:
CAnimal()
{
cout << "動物類被構造" << endl;
}
void Move()
{
cout << "動物能夠移動" << endl;
}
};
class CBird: public CAnimal //從CAnimal類繼承CBird類
{
public:
CBird()
{
cout << "鳥類被構造" << endl;
}
void FlyInSky()
{
cout << "鳥能夠在天空飛翔" << endl;
}
void Breath()
{
cout << "鳥能夠呼吸" << endl;
}
};
class CFish: public CAnimal //從CAnimal類繼承CFish
{
public:
CFish()
{
cout << "魚類被構造" << endl;
}
void SwimInWater()
{
cout << "魚能夠在水裡遊" << endl;
}
void Breath()
{
cout << "魚能夠呼吸" << endl;
}
};
class CWaterBird: public CBird, public CFish //從CBird和CFish類派生子類CWaterBird
{
public:
CWaterBird()
{
cout << "水鳥類被構造" << endl;
}
void Action()
{
cout << "水鳥即能飛又能遊" << endl;
}
};
這是一個多繼承的問題,我們在這裡沒有使用虛繼承,看一下其執行結果。
//測試代碼
CWaterBird waterBird;
waterBird.FlyInSky();
waterBird.SwimInWater();
waterBird.Action();
//waterBird.Breath()無法直接通路Breath(),存在二義性
waterBird.CBird::Breath();//需指定類名來通路
waterBird.CBird::Move();//存在兩個Animal類,是以應該指定類名
waterBird.CFish::Breath();
//運作結果
//CAnimal類被構造兩次
動物類被構造
鳥類被構造
動物類被構造
魚類被構造
水鳥類被構造
鳥能夠在天空飛翔
魚能夠在水裡遊
水鳥即能飛又能遊
鳥能夠呼吸
動物能夠移動
魚能夠呼吸
由于CBird和CFish均派生自同一個父類CAnimal,而二者派生子類CWaterBird時将存在兩個CAnimal類的複制,如果想隻有一個CAnimal類那在此就需使用虛繼承了,我們在來看一下虛繼承的示例。
//虛拟多繼承
class CAnimal //定義一個動物類
{
public:
CAnimal()
{
cout << "動物類被構造" << endl;
}
void Move()
{
cout << "動物能夠移動" << endl;
}
};
class CBird: virtual public CAnimal //從CAnimal類虛繼承CBird類
{
public:
CBird()
{
cout << "鳥類被構造" << endl;
}
void FlyInSky()
{
cout << "鳥能夠在天空飛翔" << endl;
}
void Breath()
{
cout << "鳥能夠呼吸" << endl;
}
};
class CFish: virtual public CAnimal //從CAnimal類虛繼承CFish
{
public:
CFish()
{
cout << "魚類被構造" << endl;
}
void SwimInWater()
{
cout << "魚能夠在水裡遊" << endl;
}
void Breath()
{
cout << "魚能夠呼吸" << endl;
}
};
class CWaterBird: public CBird, public CFish //從CBird和CFish類派生子類CWaterBird
{
public:
CWaterBird()
{
cout << "水鳥類被構造" << endl;
}
void Action()
{
cout << "水鳥即能飛又能遊" << endl;
}
};
這裡使用了虛繼承,我們來看一下運作結果。
//測試代碼
CWaterBird waterBird;
waterBird.FlyInSky();
waterBird.SwimInWater();
waterBird.Action();
waterBird.Move();//可以直接通路Move(),此時隻有一個Animal類
waterBird.CBird::Breath();//Breath()由于兩個父類都存在,是以還是需要指定父類
waterBird.CFish::Breath();
//測試結果
//Animal類被構造一次
動物類被構造
鳥類被構造
魚類被構造
水鳥類被構造
鳥能夠在天空飛翔
魚能夠在水裡遊
水鳥即能飛又能遊
動物能夠移動
鳥能夠呼吸
魚能夠呼吸
好啦,今天我們對虛函數和虛繼承就簡單的分析到這裡了,其實平時在開發過程中我們應該盡量少用多繼承,因為多繼承會帶來很多麻煩的情況,是以能用單繼承就使用單繼承。