虛函數(shù)
在這之前,我們先聊聊虛函數(shù)部逮。虛就是代表不是真實(shí)的娜汁,可以靈活的,函數(shù)就是方法兄朋,虛函數(shù)就是用不同的策略實(shí)現(xiàn)共同的方法掐禁。虛函數(shù)是在基類中被聲明為virtual,并在派生類中重新定義的成員函數(shù),可以實(shí)現(xiàn)成員函數(shù)的動態(tài)重載傅事。通過指向派生類的基類指針或引用缕允,訪問派生類中同名覆蓋成員函數(shù)。
純虛函數(shù)
語法
將成員函;數(shù)聲明為virtual
后面加上=0
該函數(shù)沒有函數(shù)體
C++中的純虛函數(shù)蹭越,一般在函數(shù)名后使用=0作為此類函數(shù)的標(biāo)志障本。Java,C#等語言中响鹃,則直接使用abstract作為關(guān)鍵字修飾這個(gè)函數(shù)名驾霜,表示這是抽象函數(shù)。所以說买置,純虛函數(shù)不僅僅在C++中粪糙,在整個(gè)軟件語言與程序代碼里頭,是一個(gè)很重要的思想方式堕义。
在基類中不對虛函數(shù)實(shí)例化猜旬,聲明為純虛函數(shù),在派生類里去實(shí)現(xiàn)倦卖。這個(gè)好處就是留下了比較大的空間洒擦,讓你能夠在后續(xù)自由發(fā)揮完善你的業(yè)務(wù)邏輯。
這也是面向?qū)ο蟮亩鄳B(tài)特性怕膛。多態(tài)性是指相同對象收到不同消息或不同對象收到相同消息產(chǎn)生不同的實(shí)現(xiàn)熟嫩,虛函數(shù)和繼承就很好的詮釋了運(yùn)行時(shí)的多態(tài)性。
抽象類
包含純虛函數(shù)的類成為抽象類褐捻。由于抽象類包含了沒有定義的純虛函數(shù)掸茅,所以不能定義抽象類的對象。抽象類不能被實(shí)例化柠逞,既然無法創(chuàng)建該對象昧狮,為什么要去定義這個(gè)東西呢。
其實(shí)這就是抽象類的精華板壮,我們知道Java里有interface逗鸣,它是一個(gè)接口規(guī)范,凡是遵循此規(guī)范的類绰精,都必須實(shí)現(xiàn)指定的函數(shù)接口撒璧,通常是一系列接口。
可以看到代碼里f2是虛函數(shù)笨使,f3是純虛函數(shù)卿樱。
內(nèi)存分布
首先明確的是在類里面成員函數(shù)是不占內(nèi)存的,看內(nèi)存你就要牢記抓住內(nèi)存的入口地址硫椰,或者說首地址繁调,然后是偏移地址萨蚕。
從以上內(nèi)存結(jié)構(gòu)分布圖可以看出,上面是內(nèi)存分布涉馁,下面是虛表门岔。vs編譯器把虛表指針放在了內(nèi)存的開始處,也就是0地址偏移處烤送,然后再是成員變量寒随,這里還包含了字節(jié)對齊的內(nèi)容。
下面生成了虛表帮坚,這張?zhí)摫韺?yīng)的是虛指針在內(nèi)存中的分布妻往,下面列出了虛函數(shù),左側(cè)的0是這個(gè)虛函數(shù)的序號试和,這里只有一個(gè)虛函數(shù)讯泣,所以只有一項(xiàng),如果有多個(gè)虛函數(shù)阅悍,會有序號為1好渠,為2的虛函數(shù)列出來。編譯器是在構(gòu)造函數(shù)創(chuàng)建這個(gè)虛表指針以及虛表的节视。
編譯器當(dāng)創(chuàng)建一個(gè)含有虛函數(shù)的父類的對象是拳锚,編譯器在對象構(gòu)造時(shí)將虛表指針指向父類的虛函數(shù);同樣寻行,當(dāng)創(chuàng)建子類的對象時(shí)霍掺,編譯器在構(gòu)造函數(shù)里將虛表指針(子類只有一個(gè)虛表指針,它來自父類)指向子類的虛表(這個(gè)虛表里面的虛函數(shù)入口地址是子類的)拌蜘。