C++對象模型(2)

本文預(yù)覽:

  • 關(guān)于vptr(虛函數(shù)表指針)和vtbl(虛函數(shù)表)
  • 關(guān)于this指針
  • 關(guān)于Dynamic Binding(動(dòng)態(tài)綁定)
  • new delete操作符重載

關(guān)于vptr(虛函數(shù)表指針)和vtbl(虛函數(shù)表)

虛函數(shù)表指針和虛函數(shù)表是C++實(shí)現(xiàn)多態(tài)的核心機(jī)制浊洞,理解vtbl和vptr的原理是理解C++對象模型的重要前提牵敷。
class里面method分為兩類:virtual 和non-virtual。非虛函數(shù)在編譯器編譯是靜態(tài)綁定的法希,所謂靜態(tài)綁定枷餐,就是編譯器直接生成JMP匯編代碼,對象在調(diào)用的時(shí)候直接跳轉(zhuǎn)到JMP匯編代碼執(zhí)行苫亦,既然是匯編代碼毛肋,那么就是不能在運(yùn)行時(shí)更改的了;虛函數(shù)的實(shí)現(xiàn)是通過虛函數(shù)表屋剑,虛函數(shù)表是一塊連續(xù)的內(nèi)存润匙,每個(gè)內(nèi)存單元中記錄一個(gè)JMP指令的地址,通過虛函數(shù)表在調(diào)用的時(shí)候才最終確定調(diào)用的是哪一個(gè)函數(shù)唉匾,這個(gè)就是動(dòng)態(tài)綁定趁桃。

關(guān)于vptr和vtbl

class的內(nèi)部有一個(gè)virtual函數(shù),其對象的首個(gè)地址就是vptr肄鸽,指向虛函數(shù)表卫病,虛函數(shù)表是連續(xù)的內(nèi)存空間,也就是說典徘,可以通過類似數(shù)組的計(jì)算蟀苛,就可以取到多個(gè)虛函數(shù)的地址,還有一點(diǎn)逮诲,虛函數(shù)的順序和其聲明的順序是一直的帜平。

通過虛函數(shù)表來調(diào)用虛函數(shù),繞過private的限制:

typedef void(*Fun)(void);

class Base {
private:
    virtual void f() {cout<<"Base::f()"<<endl;}
    virtual void g() {cout<<"Base::g()"<<endl;}
    virtual void h() {cout<<"Base::h()"<<endl;}

}

int main()
{
  Base b;
  Fun fp = nullprt;
  for(int i = 0; i < 3; i++)
  {
      fp = (Fun)*((long*)*(long*)(&b) + i);
      fp();
  }
}
運(yùn)行結(jié)果:
Base::f()
Base::g()
Base::h()

需要注意的是梅鹦,由于我的電腦是64位的系統(tǒng)裆甩,vptr轉(zhuǎn)成long,八個(gè)字節(jié)齐唆,32位的int就可以了嗤栓,這個(gè)根據(jù)自己的環(huán)境去修改就可以了。&b取vptr箍邮,轉(zhuǎn)成long*,取出來是vtbl 茉帅,同樣需要轉(zhuǎn)成八字節(jié),不然在指針偏移的時(shí)候肯定就錯(cuò)了锭弊,也就是+i堪澎,虛函數(shù)地址取出來要轉(zhuǎn)換成可執(zhí)行的函數(shù)指針,這樣即使在class聲明的時(shí)候做了private限制味滞,在指針面前直接就繞過去了樱蛤。

關(guān)于this指針

上一張很久之前的圖了:

關(guān)于this指針

每一個(gè)成員函數(shù)都有一個(gè)默認(rèn)參數(shù)钮呀,那就是this,this代表對象本身昨凡,但是this究竟是什么呢爽醋?this就是對象的地址。

關(guān)于Dynamic Binding(動(dòng)態(tài)綁定)

怎么理解動(dòng)態(tài)綁定和靜態(tài)綁定土匀,一般來說,對于類成員函數(shù)(不論是靜態(tài)還是非靜態(tài)的成員函數(shù))都不需要?jiǎng)?chuàng)建一個(gè)在運(yùn)行時(shí)的函數(shù)表來保存形用,他們直接被編譯器編譯成匯編代碼就轧,這就是所謂的靜態(tài)綁定;所謂動(dòng)態(tài)綁定就是對象在被創(chuàng)建的時(shí)候田度,在它運(yùn)行的時(shí)候妒御,其所攜帶的虛函數(shù)表,決定了需要調(diào)用的函數(shù)镇饺,也就是說乎莉,程序在編譯完之后是不知道的,要在運(yùn)行時(shí)才能決定到底是調(diào)用哪一個(gè)函數(shù)奸笤。這就是所謂的靜態(tài)綁定和動(dòng)態(tài)綁定惋啃。
參考: C++this指針-百度百科

動(dòng)態(tài)綁定需要三個(gè)條件同時(shí)成立:

1 指針調(diào)用
2 up-cast (有向上轉(zhuǎn)型,父類指針指向子類對象)
3 調(diào)用的是虛函數(shù)

通過兩張圖看看匯編代碼:

靜態(tài)綁定

a.vfunc1()調(diào)用虛函數(shù)监右,那么a調(diào)用的是A的虛函數(shù)边灭,還是B的虛函數(shù)?對象調(diào)用不會發(fā)生動(dòng)態(tài)綁定健盒,只有指針調(diào)用才會發(fā)生動(dòng)態(tài)綁定绒瘦。120行下面發(fā)生的call是匯編指令,call后面是一個(gè)地址扣癣,也就是函數(shù)編譯完成之后的地址了惰帽。

再看第二張:

動(dòng)態(tài)綁定

up-cast、指針調(diào)用父虑、虛函數(shù)三個(gè)條件都滿足動(dòng)態(tài)調(diào)用该酗,call指令后面不再是靜態(tài)綁定簡單的地址,翻譯成C語言大概就是(*(p->vptr)[n](p))士嚎,通過虛函數(shù)表來調(diào)用函數(shù)垂涯。

new delete操作符重載

舉個(gè)例子:

String* s = new String("hello world");

new 操作符主要干了兩件事

  • 調(diào)用operator new分配內(nèi)存空間
  • 調(diào)用構(gòu)造函數(shù)

這個(gè)在之前的文章中C++如何設(shè)計(jì)一個(gè)類2(含指針的類)將過,這里寫的就簡單一些了航邢。

這里我們要重載operator new耕赘,需要注意的是我們可以重載全局和成員操作符,兩個(gè)影響范圍是不一樣的膳殷。

void* myMalloc(size_t size)
{
    void* p = malloc(size);
    std::cout<<"myMalloc()"<<std::endl;
    return p;
}

void myFree(void* ptr)
{
    free(ptr);
    std::cout<<"myFree()"<<std::endl;
}

//會覆蓋掉前面操骡,影響范圍是全局的
void* operator new(size_t size){return myMalloc(size);}
void operator delete(void* ptr) { myFree(ptr);}

class Apple{
public:
    Apple(){std::cout<<"Apple::Apple()"<<std::endl;}
    void* operator new(size_t size){std::cout<<"+++++++"<<std::endl; return myMalloc(size);}
    void operator delete(void* ptr) {std::cout<<"-------"<<std::endl;return myFree(ptr);}
};
int main(int argc, const char * argv[]) {
    
    //調(diào)用class重載的
    Apple* apple = new Apple;
    
    delete apple;
    
    //強(qiáng)制調(diào)用全局的
    Apple* app = ::new Apple;
    
    ::delete app;

    return 0;
}

new[] 和delete[]是一個(gè)道理九火。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市册招,隨后出現(xiàn)的幾起案子岔激,更是在濱河造成了極大的恐慌,老刑警劉巖是掰,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件虑鼎,死亡現(xiàn)場離奇詭異,居然都是意外死亡键痛,警方通過查閱死者的電腦和手機(jī)炫彩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來絮短,“玉大人江兢,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵终蒂,是天一觀的道長。 經(jīng)常有香客問我叔磷,道長,這世上最難降的妖魔是什么奖磁? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任世澜,我火速辦了婚禮,結(jié)果婚禮上署穗,老公的妹妹穿的比我還像新娘寥裂。我一直安慰自己,他們只是感情好案疲,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布封恰。 她就那樣靜靜地躺著,像睡著了一般褐啡。 火紅的嫁衣襯著肌膚如雪诺舔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天备畦,我揣著相機(jī)與錄音低飒,去河邊找鬼。 笑死懂盐,一個(gè)胖子當(dāng)著我的面吹牛褥赊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播莉恼,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼拌喉,長吁一口氣:“原來是場噩夢啊……” “哼速那!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起尿背,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤端仰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后田藐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荔烧,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年汽久,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鹤竭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,747評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡回窘,死狀恐怖诺擅,靈堂內(nèi)的尸體忽然破棺而出市袖,到底是詐尸還是另有隱情啡直,我是刑警寧澤,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布苍碟,位于F島的核電站酒觅,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏微峰。R本人自食惡果不足惜舷丹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蜓肆。 院中可真熱鬧颜凯,春花似錦、人聲如沸仗扬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽早芭。三九已至彼城,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間退个,已是汗流浹背募壕。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留语盈,地道東北人舱馅。 一個(gè)月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像刀荒,于是被迫代替她去往敵國和親习柠。 傳聞我的和親對象是個(gè)殘疾皇子匀谣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評論 2 350

推薦閱讀更多精彩內(nèi)容