C++ 虛函數(shù)及虛函數(shù)表詳解
多態(tài)”的關(guān)鍵在于通過基類指針或引用調(diào)用一個虛函數(shù)時,編譯時不確定到底調(diào)用的是基類還是派生類的函數(shù),運行時才確定。
#include <iostream> using namespace std; class A { public: int i; virtual void func() {} virtual void func2() {} }; class B : public A { int j; void func() {} }; int main() { cout << sizeof(A) << ", " << sizeof(B); //輸出 8,12 return 0; }
在 32 位編譯模式下,程序的運行結(jié)果是:
8, 12
如果將程序中的 virtual 關(guān)鍵字去掉,輸出結(jié)果變?yōu)椋?br />
4, 8
A * p = new B() 實現(xiàn)多態(tài)
對比發(fā)現(xiàn),有了虛函數(shù)以后,對象所占用的存儲空間比沒有虛函數(shù)時多了 4 個字節(jié)。實際上,任何有虛函數(shù)的類及其派生類的對象都包含這多出來的 4 個字節(jié),這 4 個字節(jié)就是實現(xiàn)多態(tài)的關(guān)鍵——它位于對象存儲空間的最前端,其中存放的是虛函數(shù)表的地址。
每一個有虛函數(shù)的類(或有虛函數(shù)的類的派生類)都有一個虛函數(shù)表,該類的任何對象中都放著該虛函數(shù)表的指針(可以認(rèn)為這是由編譯器自動添加到構(gòu)造函數(shù)中的指令完成的)。
沒有覆蓋時的子類,可以看到子類的虛函數(shù)表的前面是基類離得虛函數(shù)
有覆蓋就是
子類對象地址為什么能賦值給父類對象指針?
因為,子類對象地址賦值給父類對象指針,父類對象指針就指向了子類的對象空間,父類操作子類的范圍是有限制的,只能操作到子類中父類的范圍。
基類和子類各有自己的虛函數(shù)表vtbl;不管是基類還是子類實例都會在其內(nèi)存的開頭自動創(chuàng)對象即虛函數(shù)表指針vptr, 用來訪問所在類的虛函數(shù)表
想要實現(xiàn)多態(tài),需要動態(tài)綁定,需要父類的指針或父類的引用
父類方法為虛方法,子類覆蓋父類的虛方法,才能達(dá)到多態(tài)
子類中父類沒有的方法,父類指針也無法訪問到,父類指針只能訪問到父類自己有的范圍
子類要覆蓋父類的方法,就是要函數(shù)名參數(shù)都必須一樣才叫覆蓋
再看一個例子
class A { public: virtual void vfunc1(); virtual void vfunc2(); void func1(); void func2(); private: int m_data1, m_data2; }; class B : public A { public: virtual void vfunc1(); void func1(); private: int m_data3; }; class C: public B { public: virtual void vfunc2(); void func2(); private: int m_data1, m_data4; };
子類繼承父類,子類中有父類的同名方法,訪問的是子類的方法,子類會隱藏父類所有的同名方法,即使父類有一個同名的參數(shù)不同的方法也是如此。
多重繼承(無虛函數(shù)覆蓋)
下面,再讓我們來看看多重繼承中的情況,假設(shè)有下面這樣一個類的繼承關(guān)系。注意:子類并沒有覆蓋父類的函數(shù)。
對于子類實例中的虛函數(shù)表,是下面這個樣子:
我們可以看到:
1) 每個父類都有自己的虛表。
2) 子類的成員函數(shù)被放到了第一個父類的表中。(所謂的第一個父類是按照聲明順序來判斷的)
這樣做就是為了解決不同的父類類型的指針指向同一個子類實例,而能夠調(diào)用到實際的函數(shù)。
多重繼承(有虛函數(shù)覆蓋)
下面我們再來看看,如果發(fā)生虛函數(shù)覆蓋的情況。
下圖中,我們在子類中覆蓋了父類的f()函數(shù)。
下面是對于子類實例中的虛函數(shù)表的圖:
我們可以看見,三個父類虛函數(shù)表中的f()的位置被替換成了子類的函數(shù)指針。這樣,我們就可以任一靜態(tài)類型的父類來指向子類,并調(diào)用子類的f()了。
任何妄圖使用父類指針想調(diào)用子類中的未覆蓋父類的成員函數(shù)的行為都會被編譯器視為非法,
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
C語言中strcpy()函數(shù)的具體實現(xiàn)及注意事項
C語言庫函數(shù)char *strcpy(char *dest, const char *src)把src所指向的字符串復(fù)制到dest,下面這篇文章主要給大家介紹了關(guān)于C語言中strcpy()函數(shù)的具體實現(xiàn)及注意事項的相關(guān)資料,需要的朋友可以參考下2022-11-11關(guān)于C++內(nèi)存中字節(jié)對齊問題的詳細(xì)介紹
本篇文章是對C++內(nèi)存中字節(jié)對齊的問題進(jìn)行了詳細(xì)的分析與總結(jié)。需要的朋友參考下2013-05-05C或C++報錯:ld returned 1 exit status報錯的原因及解
這篇文章主要介紹了C或C++報錯:ld returned 1 exit status報錯的原因及解決方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-02-02VSCODE+cmake配置C++開發(fā)環(huán)境的實現(xiàn)步驟
這篇文章主要介紹了VSCODE+cmake配置C++開發(fā)環(huán)境的實現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03