通常,編譯器處理虛函數的方法是:給每個對象添加一個隐藏成員,隐藏成員中儲存了一個指向函數位址數組(虛函數表)的指針,虛函數表中存儲了為類對象進行聲明的虛函數的位址,例如:
class Scientist {
...
char name[40];
public:
virtual void show_name();
virtual void show_all();
};
class Physicist : public Scientist
{
...
char field[40];
public:
void show_all(); //重寫父類方法
virtual void show_field();
...
};
類
Scientist
有一個隐藏的資料成員,裡面存儲了一個指向一張虛函數表的指針,該虛函數表記錄了類
Scientist
定義的虛函數的位址;同理類
Physicist
也同樣有一個隐藏的資料成員,裡面存儲了一個指向一張虛函數表的指針,該虛函數表記錄了類
Physicist
繼承自
Scientist
的虛函數的位址和自己新定義虛函數的位址。
調用虛函數時,程式将檢視存儲在對象中的虛函數表位址,然後通過查詢虛函數表轉向相應的虛函數,是以虛函數相比于非虛函數存在一些缺點(凡是都具有兩面性):
- 每個對象都将增大,增大量為存儲位址的空間;
- 對于每個類,編譯器都建立一個虛函數位址表(數組);
- 對于每個函數調用,都需要執行一項額外的操作,即到表中查找位址
雖然非虛函數的效率比虛函數稍高,但不具備動态聯編功能。
虛函數注意事項:
- 在基類方法的聲明中使用關鍵字
可使該方法在基類以及所有的派生類(包括派生類派生出來的類)中是虛的;virtual
- 如果使用指向對象的引用或指針來調用虛方法,程式将使用對象類型定義的方法,而不使用為引用或指針類型定義的方法,即非虛看引用或指針類型,虛看對象類型;
- 如果定義的類被用作基類,則應将那些要在派生類中重新定義的類方法聲明為虛的;
- 構造函數不能是虛函數;
- 析構函數應當是虛函數,除非類不用做基類
class Singer : public Employee
{
...
}
Employee * pe = new Singer;
delete pe;
如果使用靜态聯編 ,
delete pe;
将根據指針類型調用類
Employee
的析構函數
~Employee()
,這樣隻會釋放類
Singer
從類
Employee
中繼承過來的那一部分,自身的那一部分因為沒有調用自身的析構函數而不會被釋放;
如果使用動态聯編,
delete pe;
将根據對象類型先調用類
Singer
的析構函數
~Singer()
,釋放屬于類
Singer
的那一部分,然後再調用類
Employee
的析構函數
~Employee()
,釋放屬于類
Employee
的那一部分;
- 友元函數不能是虛函數,因為友元函數不是類成員,而隻有成員才能是虛函數,如果因為這個原因引起的設計問題,可以通過讓友元函數使用虛成員函數來解決;
- 如果派生類沒有重新定義函數,将使用該函數的基類版本。如果派生類位于派生鍊中,則将使用最新的虛函數版本,例外的情況是基類版本是隐藏的;
- 重新定義将隐藏方法
class Dweling
{
public:
virtual void showperks(int a) const;
...
};
class Hovel : public Dwelling
{
public:
virtual void showperks() const;
}
Hovel trump;
trump.showperks(); //valid
trump.showperks(5); //invalid
新定義将
showperks()
定義為一個不接受任何參數的函數,重新定義不會生成函數的兩個重載版本,而是隐藏了接受一個int參數的基類版本。
- 如果重新定義繼承的方法,應確定與原來的原型完全相同,但如果傳回類型是基類引用或指針,則可以修改為指向派生類的引用或指針(注意:這種例外隻适應于傳回值,而不适用于參數)
class Dweling
{
public:
virtual Dweling & showperks(int a) const;
...
};
class Hovel : public Dwelling
{
public:
virtual Hovel & showperks(int a) const;
}
- 如果基類聲明被重載了,則應在派生類中重新定義所有的基類版本
class Dweling
{
public:
virtual void showperks(int a) const;
virtual void showperks(double x) const;
virtual void showperks() const;
...
};
class Hovel : public Dwelling
{
public:
virtual void showperks(int a) const;
virtual void showperks(double x) const;
virtual void showperks() const;
}