1. 程式人生 > >c++虛擬函式表與虛解構函式

c++虛擬函式表與虛解構函式

由於本人才疏學淺,本文難免存在遺漏之處,歡迎大家留言指正,本人將感激不盡。

C++虛擬函式表與虛解構函式

1.靜態聯編和動態聯編

聯編:將原始碼中的函式呼叫解釋為要執行函式程式碼。

靜態聯編:編譯時能確定唯一函式。
在C中,每個函式名都能確定唯一的函式程式碼。
在C++中,因為有函式過載,編譯器須根據函式名,引數才能確定唯一的函式程式碼。

動態聯編:編譯時不能確定呼叫的函式程式碼,執行時才能。
C++中因為多型的存在,有時編譯器不知道使用者將選擇哪種型別的物件,因此無法確定呼叫的唯一性,只有在執行時才能確定。

2.虛成員函式,指標或引用,動態聯編

指標或引用才能展現類的多型性。
當類中的方法宣告為virtual時,使用指標或引用呼叫該方法,就是動態聯編。
若是普通方法,則為靜態聯編。
示例如下:

class Test
{
public:
    virtual show()
    {
        std::cout<<"Test::show()"<<std::endl;
    }
};
class SubTest:public Test
{
public:
    virtual show()
    {
        std::cout<<"SubTest::show()"<<std::endl;
    }
};
int main()
{
    SubTest subTest;
    Test * p = &subTest;//指向子類的指標
    Test & a = subTest;//子類的引用
    Test * p2 = new Test;//指向父類的指標
    p->show();
    a.show();
    p2->show();
    return 0;
}

程式沒有釋放記憶體,我們將在後面解構函式的時候,完善該程式。

3.動態聯編使用原則

動態聯編,需要跟蹤基類指標或引用指向的實際物件型別,因此效率低於靜態聯編。
1)當類不會用作基類時,成員函式不要宣告為virtual
2)當成員函式不重新定義基類的方法,成員函式不要宣告為virtual

4.關於虛擬函式

1)父類成員函式若宣告為virtual,則子類中也是虛的,若要重新定義該方法,可顯式加上virtual關鍵字,也可不加,編譯器編譯時會自動加上。
2)使用指向物件的引用或指標來呼叫虛方法,將使用具體物件型別定義的方法,而不一定是引用或指標型別定義的方法。
SubTest subTest;
Test * p = &subTest;//指向子類的指標
p->show();//將呼叫SubTest物件定義的show()方法

5.虛擬函式的工作原理

當類中存在虛擬函式時,編譯器預設會給物件新增一個隱藏成員。該成員為一個指向虛擬函式表(virtual function table,vtbl)的指標。
虛擬函式表是一個儲存了虛擬函式地址的陣列。編譯器會檢查類中所有的虛擬函式,依次將每個虛擬函式的地址,存入虛擬函式表。

class Test
{
public:
    virtual show()
    {
        std::cout<<"Test::show()"<<std::endl;
    }
private:
    int a;
};
class SubTest:public Test
{
public:
    virtual show()
    {
        std::cout<<"SubTest::show()"<<std::endl;
    }
};

記憶體結構圖如下所示:

在這裡插入圖片描述

可以看出,父類和子類有獨立的虛擬函式表,且虛擬函式表中虛擬函式指標也指向各自的虛擬函式地址,
若子類沒有覆蓋父類中的show方法,則虛擬函式指標show_ptr指向的虛擬函式show()的地址是一樣的,均指向父類show()函式地址。虛擬函式表的存在和動態聯編,就是多型的原理。

6.虛解構函式

1)建構函式是特殊的,是沒有虛擬函式的概念的。
建構函式是不繼承的,建立子類物件時,將呼叫子類的建構函式,子類的建構函式將自動呼叫父類的建構函式。

2)解構函式應是虛擬函式,除非類不用做基類。
我們看下面的程式碼:

Test *p = new SubTest;
delete p;
p=NULL;

由虛擬函式表,我們知道,若解構函式不宣告為virtual,則呼叫的將是Test類的解構函式,而沒有呼叫SubTest類的解構函式,此時造成了記憶體洩露。
所以解構函式必須宣告為虛擬函式,呼叫的將是子類SubTest的解構函式,
我們還需要知道的一點是,子類解構函式,一定會呼叫父類解構函式,釋放父類物件,則記憶體安全釋放