C++虛函數

1、指向子對象的父類指針

這個例子遇到的問題,用虛函數就可以解決

class Father
{
public:
	~Father(){cout<<"Father析構"<<endl;}
	void drive(){cout<<"爸爸會開車"<<endl;}
	void Occupation(){cout<<"爸爸是工人"<<endl;}
};

class Son:public Father
{
public:
	~Son(){cout<<"Son析構"<<endl;}
	void drive(){cout<<"兒子會騎車"<<endl;}
	void Occupation(){cout<<"兒子是學生"<<endl;}
};

int main()
{
	Father *f = new Son();

	//這裏調用的是父類的drive函數,但是指針實際是指向Son類的,卻沒有調用Son的drive函數??
	f->drive();

	//這裏father對象會被釋放掉,但是Son不會被釋放,造成內存泄露
	delete f;

	return 0;
}

2、虛函數
當類中有虛函數時,編譯器就會記錄虛函數的地址,並且在構造對象的時候建立好對象和虛函數的聯繫;這樣在調用虛函數的時候就可以根據父類指針指向的對象類型來調用相匹配的虛函數,因爲要判斷對象類型,所以這個過程有一定的資源開銷,所以虛函數比非虛函數的開銷大。
(1)虛函數必須在父類定義才能實現其作用,而且虛函數特性只對引用或者指針有效。
(2)虛函數必須是基類的非靜態成員函數。
(3)聲明函數的時候用virtual關鍵字,定義的時候不需要使用virtual關鍵字。
(4)如果子類覆蓋了父類的虛函數,不管子類中的此函數有沒有virtual關鍵字,都被視爲虛函數。
class Father
{
public:
	//virtual關鍵字說明這個函數是虛函數
	virtual void drive(){cout<<"爸爸會開車"<<endl;}
};

class Son:public Father
{
public:
	//子類函數覆蓋父類函數,這個函數也變成了虛函數,不管有沒有virtual聲明
	void drive(){cout<<"兒子會騎車"<<endl;}
};

int main()
{
	Father *f = new Son();

	//父類的drive是虛函數,所以這裏會調用子類的drive函數
	f->drive();

	return 0;
}

3、多態性
多態性是指同一類的對象接收相同的消息,會得到不同的結果。虛函數的特性就體現了多態性。
注:C++規定必須使用基類的指針或者引用來操作同一對象,然後當收到消息時才確定指向哪個具體的對象。
class Base
{
public:
	virtual void print(){cout<<"A"<<endl;}
};

class A:public Base
{
public:
	void print(){cout<<"A"<<endl;}
};

class B:public Base
{
public:
	void print(){cout<<"B"<<endl;}
};

class C:public Base
{
public:
	void print(){cout<<"C"<<endl;}
};

int main()
{
	//同一類對象base,接受相同消息print,得到不同結果A、B、C
	Base *base = new A;
	base->print();

	base = new B;
	base->print();

	base = new C;
	base->print();

	return 0;
}

4、虛析構函數
當創建一個派生類對象的時候,會先調用基類的構造函數構造這個對象的基類部分,然後調用派生類的構造函數構造其餘部分。
一般情況下,使用虛函數的時候,我們會把派生類的地址賦給基類指針,這時如果基類的析構函數是非虛析構函數,那麼在執行刪除操作的時候,它只會刪除基類對象,子類對象不會被刪除,從而造成內存泄露。
上述情況,需要把基類的析構函數聲明爲虛析構函數,這樣就具備了多態性,在執行刪除操作的時候,首先會判斷基類指針指向哪個對象,然後調用這個對象的析構函數,由於派生類的析構函數會自動調用基類的析構函數,所以整個對象會被正確銷燬。
注:只要基類是虛析構函數,那麼派生類的析構函數也是虛析構函數,不管派生類自己有沒有聲明,這一點和普通的虛函數是一樣的。
class Father
{
public:
	//虛析構函數
	virtual ~Father(){cout<<"Father析構"<<endl;}
};

class Son:public Father
{
public:
	virtual ~Son(){cout<<"Son析構"<<endl;}
};

int main()
{
	Father *f = new Son();
	
	//先析構子類,再析構父類
	delete f;
	return 0;
}

5、在虛函數中使用成員名限定
在虛函數中使用成員名限定可以強制解除動態聯編(即虛函數的多態性)

class Father
{
public:
	//虛析構函數
	virtual ~Father(){cout<<"Father析構"<<endl;}
	virtual void drive(){cout<<"爸爸會開車"<<endl;}
};

class Son:public Father
{
public:
	~Son(){cout<<"Son析構"<<endl;}
	void drive(){cout<<"兒子會騎車"<<endl;}
};

int main()
{
	Father *f = new Son();
	
	//調用子類drive函數
	f->drive();

	//作用域限定符強制調用父類drive函數
	f->Father::drive();
	
	delete f;
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章