天天看點

C/C++虛函數與虛繼承

之前一篇博文我們分析了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
           

好啦到此虛函數我們就通過一個示例簡單的分析了一下,但是虛函數在使用的過程中有以下幾個方面的限制。

  1. 隻有類的成員函數才能是虛函數
  2. 靜态成員函數不能是虛函數
  3. 内聯函數不能是虛函數
  4. 構造函數不能是虛函數,析構函數通常是虛函數

##虛繼承##

我們先來看一個沒有虛繼承的示例

//普通多繼承
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類被構造一次
動物類被構造
鳥類被構造
魚類被構造
水鳥類被構造

鳥能夠在天空飛翔
魚能夠在水裡遊
水鳥即能飛又能遊

動物能夠移動
鳥能夠呼吸
魚能夠呼吸
           

好啦,今天我們對虛函數和虛繼承就簡單的分析到這裡了,其實平時在開發過程中我們應該盡量少用多繼承,因為多繼承會帶來很多麻煩的情況,是以能用單繼承就使用單繼承。