【C++】虛函數、純虛函數、多態與虛表機制詳解

虛函數

在類的定義中,前面有 virtual 關鍵字的成員函數就是虛函數。

注:派生類中的成員函數 與 基類中虛函數同名且同參數的函數,不加virtual也自動成爲虛函數。

純虛函數與抽象類

沒有函數體的虛函數稱作純虛函數,包含純虛函數的類叫抽象類

注:

  • 抽象類只能作爲基類來派生新類使用,不能創建抽象類的對象。
  • 抽象類的指針和引用可以指向由抽象類派生出來的類的對象。
  • 在抽象類的成員函數內可以調用純虛函數,但是在構造函數或析構函數內部 不能調用純虛函數。
  • 如果一個類從抽象類派生而來,那麼當且僅當它實現了基類中的所有純虛函 數,它才能成爲非抽象類。
class A {
    private: 
        int a;
    public:
        virtual void Print( ) = 0 ; //純虛函數
        void fun() { cout << "fun"; }
};
int main()
{
    A a ; // 錯,A 是抽象類,不能創建對象
    A * pa ; // 對,可以定義抽象類的指針和引用
    pa = new A ; //錯, A 是抽象類,不能創建對象
    return 0;
}

多態

多態的實現是通過虛函數。

多態的作用是增強程序的可擴充性,即程序需要修改或增加功能的時候,需要改動和增加的代碼較少。

多態的關鍵在於通過基類指針或引用調用一個虛函數時,編譯階段不能確定到底調用的是基類還是派生類的函數,運行時才能夠確定——這叫動態聯編虛函數因爲用了虛函數表機制,調用的時候會增加內存開銷,具體見下文。

注:在非構造函數,非析構函數的成員函數中調用虛函數,是多態;在構造函數和析構函數中調用虛函數,不是多態。

補:一般多態指的是上述的動態多態,還有一種靜態多態是通過函數的重載實現的。

非虛函數靜態聯編,效率要比虛函數高,但是不具備動態聯編能力。

虛表機制

每一個有虛函數的類(或有虛函數的類的派生類)都有一個虛函數表(數組),該表列出了各個虛函數地址,該類的任何對象中都放着一個虛表指針(32位佔4字節,64位佔8字節),虛表指針會在運行時動態綁定決定該對象的虛函數地址。

那麼虛函數表是在運行時創建的還是編譯時創建的呢?虛函數表編譯的時候就確定了,而類對象的虛函數指針vptr是在運行階段確定的。(類的函數的調用並不是在編譯時就確定的,而是在運行時才確定的,由於編寫代碼的時候並不能確定被調用的是基類的函數還是哪個派生類的函數,所以聲明爲虛函數。虛函數和虛函數表是兩個不同的東西,虛函數的調用是在運行時才確定的,虛函數表是在編譯時就已經確定的了 。)

/*——參考自C++ primer一書中的案例——*/
#include "iostream"
using namespace std;
class Base {
public:
	int a;
	virtual void print1() {};
	virtual void print2() { cout << "Base" << endl; };
};
class Derived : public Base {
public:
	int b;
	void print2() { cout << "Derived" << endl; }; //redefined
	virtual void print3() {}; //new
};
int main() {
	Derived A;
	Base* B = &A;//調用派生類Derived的成員函數 輸出Derived
	B->print2();
	//驗證虛表指針佔空間32位時輸出8,12
	cout << sizeof(Base) << "," << sizeof(Derived);
	return 0;
}
類虛函數表與各虛函數地址

1、可觀察到每個類都創建一個虛函數表,其中:

  • Derived類未對繼承的Base類中的虛函數print1()進行重寫,則地址不變。
  • 重寫了print2()虛函數,地址則改變。
  • 新定義了一個虛函數,則虛函數表新增一個虛函數地址。

2、那Base類與Derived類的字節大小爲什麼是8,12呢?因爲虛表指針也會佔用一個存儲地址空間。

與此同時,調用虛函數時也要通過虛表指針來尋址,因而需要消耗一定量的時間,但這相較於沒有多態人工重寫這些函數的時間量小得多。

3、爲什麼調用Base類的指針下的虛函數,執行的是Dervied中的虛函數呢?

若使用指向對象的引用或指針調用虛方法,程序將根據對象類型來調用方法,而不是指針的類型。

如果派生類沒有重定義虛函數,則會使用基類中的虛函數。

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