C++之類解構函式為什麼是虛擬函式

林堯彬發表於2020-04-04

請說明下列程式碼的輸出,並解釋原因。

#include<stdio.h>

class A
{
public:
    virtual ~A();
};

A::~A()
{
    printf("delete A\n");
}

class B : public A
{
public:
    ~B();
};

B::~B()
{
    printf("delete B\n");
}

int main(int argc, char **argv)                                                                                                                                                 
{
    A* pa = new B();
    delete pa;
}

輸出結果:

delete B
delete A

C++中的虛擬函式的作用主要是實現了多型的機制。關於多型,簡而言之就是用父型別別的指標指向其子類的例項,然後通過父類的指標呼叫實際子類的成員函式。這種技術可以讓父類的指標有“多種形態”,這是一種泛型技術。所謂泛型技術,說白了就是試圖使用不變的程式碼來實現可變的演算法。

C++ 瞭解的人都應該知道虛擬函式(Virtual Function)是通過一張虛擬函式表(Virtual Table)來實現的。簡稱為V-Table。在這個表中,主是要一個類的虛擬函式的地址表,這張表解決了繼承、覆蓋的問題,保證其容真實反應實際的函式。這樣,在有虛擬函式的類的例項中這個表被分配在了這個例項的記憶體中,所以,當我們用父類的指標來操作一個子類的時候,這張虛擬函式表就顯得由為重要了,它就像一個地圖一樣,指明瞭實際所應該呼叫的函式。

C++的編譯器應該是保證虛擬函式表的指標存在於物件例項中最前面的位置(這是為了保證取到虛擬函式表的有最高的效能——如果有多層繼承或是多重繼承的情況下)。 這意味著我們通過物件例項的地址得到這張虛擬函式表,然後就可以遍歷其中函式指標,並呼叫相應的函式。

子類沒有過載任何父類的函式時,虛擬函式按照其宣告順序放於表中,父類的虛擬函式在子類的虛擬函式前面。

如果子類中有虛擬函式過載了父類的虛擬函式,覆蓋的函式被放到了虛表中原來父類虛擬函式的位置,沒有被覆蓋的函式依舊。

Base *b = new Derive();
b->f();

此時,b所指的記憶體中的虛擬函式表的f()的位置已經被Derive::f()函式地址所取代,於是在實際呼叫發生時,是Derive::f()被呼叫了。

任何妄圖使用父類指標想呼叫子類中的未覆蓋父類的成員函式的行為都會被編譯器視為非法,所以,這樣的程式根本無法編譯通過。但在執行時,我們可以通過指標的方式訪問虛擬函式表來達到違反C++語義的行為。

參考:

http://blog.csdn.net/haoel/article/details/1948051/

轉載於:https://www.cnblogs.com/erli11/p/3850694.html

相關文章