什么是虛函數(shù)
C++中的虛函數(shù)的作用主要是實(shí)現(xiàn)了多態(tài)的機(jī)制。關(guān)于多態(tài)隧土,簡而言之就是用父類型別的指針指向其子類的實(shí)例命爬,然后通過父類的指針調(diào)用實(shí)際子類的成員函數(shù)。這種技術(shù)可以讓父類的指針有“多種形態(tài)”
虛函數(shù)表
虛函數(shù)(Virtual Function)是通過一張?zhí)摵瘮?shù)表(Virtual Table)來實(shí)現(xiàn)的皆愉。在這個(gè)表中艇抠,主是要一個(gè)類的虛函數(shù)的地址表,這張表解決了繼承翔脱、覆蓋的問題。這樣届吁,在有虛函數(shù)的類的實(shí)例中這個(gè)表被分配在了這個(gè)實(shí)例的內(nèi)存中绿鸣,所以,當(dāng)我們用父類的指針來操作一個(gè)子類的時(shí)候亮蛔,這張?zhí)摵瘮?shù)表就顯得由為重要了擎厢,它就像一個(gè)地圖一樣,指明了實(shí)際所應(yīng)該調(diào)用的函數(shù)芬探。
C++的編譯器確保虛函數(shù)表的指針存在于對象實(shí)例中最前面的位置(確保虛函數(shù)的性能)厘惦。 這意味著我們通過對象實(shí)例的地址得到這張?zhí)摵瘮?shù)表,然后就可以遍歷其中函數(shù)指針,并調(diào)用相應(yīng)的函數(shù)节榜。
class Base {
public:
virtual void f() { }
virtual void g() { }
virtual void h() { }
};
一般繼承(無虛函數(shù)覆蓋)
在這個(gè)繼承關(guān)系中全跨,子類沒有重載任何父類的函數(shù)亿遂。那么,在派生類的實(shí)例中
1)虛函數(shù)按照其聲明順序放于表中挪钓。
2)父類的虛函數(shù)在子類的虛函數(shù)前面耳舅。
一般繼承(有虛函數(shù)覆蓋)
子類
class Derive:public Base{
void f();
}
我們從表中可以看到下面幾點(diǎn),
1)覆蓋的f()函數(shù)被放到了虛表中原來父類虛函數(shù)的位置馏予。
2)沒有被覆蓋的函數(shù)依舊。
這樣霞丧,我們就可以看到對于下面這樣的程序冕香,
Base *b = new Derive();
b->f();
由b所指的內(nèi)存中的虛函數(shù)表的f()的位置已經(jīng)被Derive::f()函數(shù)地址所取代,于是在實(shí)際調(diào)用發(fā)生時(shí)突那,是Derive::f()被調(diào)用了构眯。這就實(shí)現(xiàn)了多態(tài)。
多重繼承(無虛函數(shù)覆蓋)
1猫缭、每個(gè)父類都有自己的虛表它褪。
2翘悉、第一個(gè)父類是按照聲明順序來判斷的
多重繼承(有虛函數(shù)覆蓋)
我們在子類中覆蓋了父類的f()函數(shù)。
我們可以看見老赤,三個(gè)父類虛函數(shù)表中的f()的位置被替換成了子類的函數(shù)指針。這樣抬旺,我們就可以任一靜態(tài)類型的父類來指向子類,并調(diào)用子類的f()了
虛表的結(jié)尾
windows下以NULL為結(jié)尾
Linux下如果是多重繼承汉柒,則以1結(jié)尾责鳍,0代表沒有后續(xù)的虛表了。
虛函數(shù)的一些問題
1正塌、通過父類型的指針訪問子類自己的虛函數(shù)
任何妄圖使用父類指針想調(diào)用子類中的未覆蓋父類的成員函數(shù)的行為都會被編譯器視為非法,這樣的程序根本無法編譯通過乓诽。但在運(yùn)行時(shí)咒程,我們可以通過指針的方式訪問虛函數(shù)表來達(dá)到違反C++語義的行為。
2粮宛、 訪問non-public的虛函數(shù)
另外,如果父類的虛函數(shù)是private或是protected的巍杈,但這些非public的虛函數(shù)同樣會存在于虛函數(shù)表中扛伍,所以,我們同樣可以使用訪問虛函數(shù)表的方式來訪問這些non-public的虛函數(shù)鳖宾,這是很容易做到的。
虛函數(shù)的效率
虛函數(shù)需要一次間接的尋鼎文。而一般的函數(shù)可以在編譯時(shí)定位到函數(shù)的地址因俐,而虛函數(shù)(動(dòng)態(tài)類型調(diào)用)是根據(jù)某個(gè)指針定位到函數(shù)在虛函數(shù)表的地址周偎。當(dāng)在動(dòng)態(tài)執(zhí)行時(shí)蓉坎,會到該函數(shù)表中尋找函數(shù),多增加了一個(gè)過程蛉艾,效率自然比較低一點(diǎn)衷敌。(虛函數(shù)內(nèi)部執(zhí)行時(shí)間越短,虛函數(shù)查找時(shí)間占比就越高缴罗,相對來說性能損耗就越高)