C++:从子类访问父类的私有函数
生活随笔
收集整理的這篇文章主要介紹了
C++:从子类访问父类的私有函数
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
| C++:從子類訪問(wèn)父類的私有函數(shù) 轉(zhuǎn)自 龍音閣http://blog.sina.com.cn/dragonsound 感謝原作者的工作 (2009-09-01 22:44:21) 標(biāo)簽: 虛函數(shù) c private f1 it? ? ? ???眾所周知,c和c++的數(shù)組都是不安全的,因?yàn)闊o(wú)論c還是c++都不提供數(shù)組邊界檢查功能,這使得數(shù)組溢出成為可能。從某個(gè)意義上說(shuō),c和c++是一種缺少監(jiān)督的語(yǔ)言,然而這也正是其魅力所在。c++給予程序員更大的自由,相比于使用JAVA編程的束手束腳,c++程序員擁有了更大的權(quán)力,同時(shí)也擁有更多的機(jī)遇來(lái)玩弄一些技巧,比如說(shuō),從子類調(diào)用父類的私有函數(shù)。從子類調(diào)用父類的private函數(shù)?我沒(méi)聽(tīng)錯(cuò)么? 當(dāng)然沒(méi)有! 盡管從各種c++書籍中我們得到的信息是子類從父類繼承的僅有protected成員和public成員,而父類的private成員無(wú)法被子類繼承,也無(wú)法被子類訪問(wèn),但是當(dāng)父類的private函數(shù)是一個(gè)虛函數(shù)時(shí),我們卻可以通過(guò)讀取VTABLE表中信息,從而找到父類虛函數(shù)的地址,進(jìn)而調(diào)用它。 先回憶一下,c++的多態(tài)是怎樣實(shí)現(xiàn)的。 當(dāng)c++的類中出現(xiàn)virtual關(guān)鍵字時(shí),該類就擁有了一張VTABLE表。VTABLE表的內(nèi)容包括了各個(gè)虛函數(shù)在虛擬內(nèi)存中的偏移量,也就是說(shuō)如果類A擁有三個(gè)虛函數(shù):f1,f2,f3,那么在類A的虛函數(shù)表VTABLE中將依次存放f1,f2,f3的偏移地址。 那么,當(dāng)類A僅擁有一個(gè)虛函數(shù)時(shí),類A實(shí)例所占內(nèi)存大小,也就是sizeof(A)與類A擁有兩個(gè)、三個(gè),甚至更多個(gè)虛函數(shù)時(shí)的sizeof(A)有區(qū)別么?不,毫無(wú)區(qū)別。如果一個(gè)類的所有成員都是被virtual修飾的虛函數(shù),那么當(dāng)您使用sizeof(A)查看其大小時(shí),結(jié)果無(wú)一例外的都是4——在32位系統(tǒng)中,四字節(jié)恰恰是一個(gè)整型數(shù)的大小,也恰恰是一個(gè)指針的大小。 這是為什么? 因?yàn)閷?duì)于類A而言,它并不需要知道有多少個(gè)虛函數(shù),它需要的僅僅是一個(gè)指向VTABLE的指針,這個(gè)指針通常被叫作vptr。它指向了VTABLE,至于究竟有多少個(gè)虛函數(shù),只需在VTABLE中尋找就是。讓我們想象一下,vptr就向一個(gè)路標(biāo),它指向一個(gè)名叫VTABLE的公司,至于公司里有多少員工,你必須進(jìn)入VTABLE公司才會(huì)知道。 好了,現(xiàn)在我們總結(jié)一下: 結(jié)論一:在有虛函數(shù)的類中,一定有一個(gè)vptr指向VTABLE 結(jié)論二:VTABLE中依次存儲(chǔ)了各個(gè)虛函數(shù)在虛擬內(nèi)存中的偏移地址 現(xiàn)在,我們?cè)賮?lái)介紹另兩個(gè)c++的規(guī)律。 規(guī)律一:在任何類中,vptr一定存儲(chǔ)在該類實(shí)例的前四個(gè)字節(jié)中。 規(guī)律二:當(dāng)子類和父類同時(shí)擁有虛函數(shù)時(shí),子類的VTABLE中也同時(shí)會(huì)擁有父類和子類的虛函數(shù)偏移地址,而且子類的虛函數(shù)偏移地址一定是追加在父類虛函數(shù)偏移地址之后的。 也就是說(shuō),如果有如下兩個(gè)類: class A { private: virtual void WhoAmI() { cout << "I am class A" << endl; } }; class B:public A { public: void WhoAmIForB() { cout << "I am class B" << endl; } }; 那么,實(shí)例 A a; B b; 中各有一個(gè)vptr,其中a的vptr為(int*)(*(int*)(&a)),而b的vptr為(int*)(*(int*)(&b)), 這兩個(gè)vptr又分別指向各自的VTABLE,其中父類A的VTABLE中存儲(chǔ)的內(nèi)容是:A::WhoAmI的偏移地址,而子類B的VTABLE呢? 子類B的VTABLE中依次存儲(chǔ)了A::WhoAmI的偏移地址,B::WhoAmIForB的偏移地址。 注意了,關(guān)鍵就在這里:A的虛函數(shù)都是私有的,不是么?但是編譯器鏈接器在此時(shí)卻似乎將關(guān)鍵字private忘記了,無(wú)論這些虛函數(shù)是private還是public的,它們的偏移地址都毫無(wú)例外的存放在了子類的VTABLE中! 這就是破綻!你可以刺出至命的一劍了! 我們既然知道子類實(shí)例的vptr,為什么不能推算出子類的VTABLE? 既然知道子類的VTABLE,根據(jù)規(guī)定律二,為什么不能推算出父類的虛函數(shù)偏移量? 答案就是:父類的第一個(gè)虛函數(shù)所在偏移量是(int*)(*(子類的vptr)),也就是——(int*)(*(int*)(*(int*)(&b)))。 當(dāng)我們強(qiáng)制將其轉(zhuǎn)換為一個(gè)指向函數(shù)的指針時(shí),就可以調(diào)用它,從而實(shí)現(xiàn)了從子類調(diào)用父類私有函數(shù)的行為。 試運(yùn)行如下一段代碼: #include <iostream> using namespace std; class A { private: virtual void WhoAmI() { cout << "I am class A" << endl; } virtual void f0() { cout << "This is f0" << endl; } virtual void f1() { cout << "This is f1" << endl; } }; class B:public A { public: void WhoAmIForB() { cout << "I am class B" << endl; } }; typedef void (*FUNC)(); int main(int argc,char * argv[]) { B b; b.WhoAmIForB(); //b.WhoAmI(); error C2248: “A::WhoAmI”: 無(wú)法訪問(wèn) private 成員(在“A”類中聲明) FUNC func = (FUNC)((int*)(*(int*)(*(int*)(&b)))); func(); return 0; } |
總結(jié)
以上是生活随笔為你收集整理的C++:从子类访问父类的私有函数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【推荐】LSI(latent seman
- 下一篇: C++继承机制