C++從子類訪問父類的私人函數

來源:互聯網
上載者:User
C++:從子類訪問父類的私人函數

轉自 龍音閣http://blog.sina.com.cn/dragonsound 感謝原作者的工作

(2009-09-01 22:44:21)


標籤: 虛函數 c private f1 it         眾所周知,c和c++的數組都是不安全的,因為無論c還是c++都不提供數組邊界檢查功能,這使得數組溢出成為可能。從某個意義上說,c和c++是一種缺少監督的語言,然而這也正是其魅力所在。c++給予程式員更大的自由,相比於使用JAVA編程的束手束腳,c++程式員擁有了更大的權力,同時也擁有更多的機遇來玩弄一些技巧,比如說,從子類調用父類的私人函數。從子類調用父類的private函數?我沒聽錯嗎?

當然沒有!

儘管從各種c++書籍中我們得到的資訊是子類從父類繼承的僅有protected成員和public成員,而父類的private成員無法被子類繼承,也無法被子類訪問,但是當父類的private函數是一個虛函數時,我們卻可以通過讀取VTABLE表中資訊,從而找到父類虛函數的地址,進而調用它。


先回憶一下,c++的多態是怎樣實現的。

當c++的類中出現virtual關鍵字時,該類就擁有了一張VTABLE表。VTABLE表的內容包括了各個虛函數在虛擬記憶體中的位移量,也就是說如果類A擁有三個虛函數:f1,f2,f3,那麼在類A的虛函數表VTABLE中將依次存放f1,f2,f3的位移地址。

那麼,當類A僅擁有一個虛函數時,類A執行個體所佔記憶體大小,也就是sizeof(A)與類A擁有兩個、三個,甚至更多個虛函數時的sizeof(A)有區別嗎?不,毫無區別。如果一個類的所有成員都是被virtual修飾的虛函數,那麼當您使用sizeof(A)查看其大小時,結果無一例外的都是4——在32位系統中,四位元組恰恰是一個整型數的大小,也恰恰是一個指標的大小。

這是為什嗎?

因為對於類A而言,它並不需要知道有多少個虛函數,它需要的僅僅是一個指向VTABLE的指標,這個指標通常被叫作vptr。它指向了VTABLE,至於究竟有多少個虛函數,只需在VTABLE中尋找就是。讓我們想象一下,vptr就向一個路標,它指向一個名叫VTABLE的公司,至於公司裡有多少員工,你必須進入VTABLE公司才會知道。


好了,現在我們總結一下:

結論一:在有虛函數的類中,一定有一個vptr指向VTABLE

結論二:VTABLE中依次儲存了各個虛函數在虛擬記憶體中的位移地址

現在,我們再來介紹另兩個c++的規律。

規律一:在任何類中,vptr一定儲存在該類執行個體的前四個位元組中。

規律二:當子類和父類同時擁有虛函數時,子類的VTABLE中也同時會擁有父類和子類的虛函數位移地址,而且子類的虛函數位移地址一定是追加在父類虛函數位移地址之後的。

也就是說,如果有如下兩個類:

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;

}

};


那麼,執行個體

A a;

B b;

中各有一個vptr,其中a的vptr為(int*)(*(int*)(&a)),而b的vptr為(int*)(*(int*)(&b)),

這兩個vptr又分別指向各自的VTABLE,其中父類A的VTABLE中儲存的內容是:A::WhoAmI的位移地址,而子類B的VTABLE呢?

子類B的VTABLE中依次儲存了A::WhoAmI的位移地址,B::WhoAmIForB的位移地址。


注意了,關鍵就在這裡:A的虛函數都是私人的,不是嗎?但是編譯器連結器在此時卻似乎將關鍵字private忘記了,無論這些虛函數是private還是public的,它們的位移地址都毫無例外的存放在了子類的VTABLE中!

這就是破綻!你可以刺出至命的一劍了!


我們既然知道子類執行個體的vptr,為什麼不能推算出子類的VTABLE?

既然知道子類的VTABLE,根據規定律二,為什麼不能推算出父類的虛函式位移?

答案就是:父類的第一個虛函數所在位移量是(int*)(*(子類的vptr)),也就是——(int*)(*(int*)(*(int*)(&b)))。

當我們強制將其轉換為一個指向函數的指標時,就可以調用它,從而實現了從子類調用父類私人函數的行為。


試運行如下一段代碼:

#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”: 無法訪問 private 成員(在“A”類中聲明)




FUNC func = (FUNC)((int*)(*(int*)(*(int*)(&b))));

func();


return 0;

}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.