成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

如何進(jìn)行C++虛函數(shù)分析-創(chuàng)新互聯(lián)

如何進(jìn)行C++虛函數(shù)分析,針對這個(gè)問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡單易行的方法。

成都創(chuàng)新互聯(lián)公司專注于企業(yè)全網(wǎng)整合營銷推廣、網(wǎng)站重做改版、烏蘭察布網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、HTML5建站、購物商城網(wǎng)站建設(shè)、集團(tuán)公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站建設(shè)公司、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為烏蘭察布等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。

虛函數(shù)調(diào)用屬于運(yùn)行時(shí)多態(tài),在類的繼承關(guān)系中,通過父類指針來調(diào)用不同子類對象的同名方法,而產(chǎn)生不同的效果。

C++ 中的多態(tài)是通過晚綁定(對象構(gòu)造時(shí))來實(shí)現(xiàn)的。

用法

在函數(shù)之前聲明關(guān)鍵字virtual 表示這是一個(gè)虛函數(shù),在函數(shù)后增加一個(gè)= 0 表示這是一個(gè)純虛函數(shù),純虛函數(shù)的類不能創(chuàng)建具體實(shí)例。

該示例作后文分析使用,一個(gè)包含純虛函數(shù)的父類,一個(gè)重寫了父類方法的子類,一個(gè)無繼承的類。

struct Base {  Base() : val(7777) {}  virtual int fuck(int a) = 0;  int val;};struct Der : public Base {  Der() = default;  int fuck(int a) override { return val + 4396; }};struct A {  A() = default;  void funny(int a) {}};int main() {  Der der;  Base *pbase = &der;  pbase->fuck(sizeof(Der)); // 調(diào)用 Der::fuck(int a);  A a;  a.funny(sizeof(A)); // A::funny(int a);  return 3;}

實(shí)現(xiàn)

原來就了解虛函數(shù)是通過虛表的偏移來獲取實(shí)際調(diào)用函數(shù)地址來實(shí)現(xiàn)的,但是在何時(shí)確定這個(gè)偏移和具體的偏移細(xì)節(jié)也沒有說明,今兒個(gè)來探探究竟。

拿上面的代碼進(jìn)行反匯編獲提取部分函數(shù),main,Base::Base(), Base::fuck(), Der::Der(), Der::fuck, A::funny() 如下:

_ZN4BaseC2Ev:.LFB1:  .cfi_startproc  pushq  %rbp  .cfi_def_cfa_offset 16  .cfi_offset 6, -16  movq  %rsp, %rbp  .cfi_def_cfa_register 6  movq  %rdi, -8(%rbp)  // 還是 main 函數(shù)的棧幀 -32(%rpb) 的地址  leaq  16+_ZTV4Base(%rip), %rdx // 關(guān)鍵點(diǎn)來了,取虛表偏移 16 的地址也就是 __cxa_pure_virtual,這里是沒有意義的  movq  -8(%rbp), %rax  movq  %rdx, (%rax)   // 將 __cxa_pure_virtual 的地址存放在 地址rax 的內(nèi)存中(這個(gè)例子中也就是main 函數(shù)的棧幀 -32(%rpb) 的地方),   movq  -8(%rbp), %rax  // 然后往后偏移 8 個(gè)字節(jié),也就是跳過虛表指針,對成員變量 val 初始化。  movl  $7777, 8(%rax)  nop           // 注:上面是用這個(gè)示例中實(shí)際的地址帶入的,實(shí)際上對于一個(gè)有的類的處理是一個(gè)通用邏輯的,構(gòu)造函數(shù)傳入的第一個(gè)參數(shù) rdi 是 this 指針,由于有虛表存在的影響,這里會(huì)修改 this 指針?biāo)诘刂返膬?nèi)容,也就是虛表的偏移地址(非起始地址)  popq  %rbp  .cfi_def_cfa 7, 8  ret  .cfi_endproc.LFE1:  .size  _ZN4BaseC2Ev, .-_ZN4BaseC2Ev  .weak  _ZN4BaseC1Ev  .set  _ZN4BaseC1Ev,_ZN4BaseC2Ev  .section  .text._ZN3Der4fuckEi,"axG",@progbits,_ZN3Der4fuckEi,comdat  .align 2  .weak  _ZN3Der4fuckEi  .type  _ZN3Der4fuckEi, @function_ZN3Der4fuckEi:.LFB3:  .cfi_startproc  pushq  %rbp  .cfi_def_cfa_offset 16  .cfi_offset 6, -16  movq  %rsp, %rbp  .cfi_def_cfa_register 6  movq  %rdi, -8(%rbp)  movl  %esi, -12(%rbp)  movq  -8(%rbp), %rax  movl  8(%rax), %eax  // 成員變量 val,val 是從 rdi 中偏移 8 字節(jié)取的值  addl  $4396, %eax   // val + 4396  popq  %rbp  .cfi_def_cfa 7, 8  ret  .cfi_endproc.LFE3:  .size  _ZN3Der4fuckEi, .-_ZN3Der4fuckEi  .section  .text._ZN1A5funnyEi,"axG",@progbits,_ZN1A5funnyEi,comdat  .align 2  .weak  _ZN1A5funnyEi  .type  _ZN1A5funnyEi, @function_ZN1A5funnyEi:.LFB4:  .cfi_startproc  pushq  %rbp  .cfi_def_cfa_offset 16  .cfi_offset 6, -16  movq  %rsp, %rbp  .cfi_def_cfa_register 6  movq  %rdi, -8(%rbp)  movl  %esi, -12(%rbp)  nop  popq  %rbp  .cfi_def_cfa 7, 8  ret  .cfi_endproc.LFE4:  .size  _ZN1A5funnyEi, .-_ZN1A5funnyEi  .section  .text._ZN3DerC2Ev,"axG",@progbits,_ZN3DerC5Ev,comdat  .align 2  .weak  _ZN3DerC2Ev  .type  _ZN3DerC2Ev, @function_ZN3DerC2Ev:.LFB7:  .cfi_startproc  pushq  %rbp  .cfi_def_cfa_offset 16  .cfi_offset 6, -16  movq  %rsp, %rbp  .cfi_def_cfa_register 6  subq  $16, %rsp  movq  %rdi, -8(%rbp)  // rdi 是取的 main 棧幀 -32(%rbp) 的地址  movq  -8(%rbp), %rax  movq  %rax, %rdi  call  _ZN4BaseC2Ev   // Base 的構(gòu)造函數(shù),并且又把傳進(jìn)來的參數(shù)作為實(shí)參傳進(jìn)去了,這里跟蹤進(jìn)去  leaq  16+_ZTV3Der(%rip), %rdx // 取虛表偏移16字節(jié) _ZN3Der4fuckEi 的地址   movq  -8(%rbp), %rax  movq  %rdx, (%rax)   // rax 在之前的 Base構(gòu)造函數(shù)中是被修改了的,這里將繼續(xù)修改內(nèi)容,前一次的修改失效。  nop  leave  .cfi_def_cfa 7, 8  ret  .cfi_endproc.LFE7:  .size  _ZN3DerC2Ev, .-_ZN3DerC2Ev  .weak  _ZN3DerC1Ev  .set  _ZN3DerC1Ev,_ZN3DerC2Ev  .text  .globl main  .type  main, @functionmain:.LFB5:  .cfi_startproc  pushq  %rbp  .cfi_def_cfa_offset 16  .cfi_offset 6, -16  movq  %rsp, %rbp  .cfi_def_cfa_register 6  subq  $48, %rsp  leaq  -32(%rbp), %rax // 取 -32(%rbp) 的地址,對應(yīng) Base *pbase;  movq  %rax, %rdi  call  _ZN3DerC1Ev   // 調(diào)用了構(gòu)造函數(shù),并且以-32(%rbp) 的地址作為參數(shù),這里跟蹤進(jìn)去  leaq  -32(%rbp), %rax // -32(%rbp) 被修改,該內(nèi)存中的內(nèi)容為 Der 虛表的偏移地址   movq  %rax, -8(%rbp)  movq  -8(%rbp), %rax  movq  (%rax), %rax  // rax = M[rax],取出虛表偏移中的地址  movq  (%rax), %rdx  // rdx = M[rax] , 取出虛表偏移的內(nèi)容(也就是函數(shù)地址),算上上面這是做了兩次解引用  movq  -8(%rbp), %rax  movl  $16, %esi    // sizeof(Der) = 16, 包含一個(gè)虛表指針和 int val;  movq  %rax, %rdi   // 虛表偏移中的地址  call  *%rdx      // 調(diào)用函數(shù)  leaq  -33(%rbp), %rax  movl  $1, %esi  movq  %rax, %rdi  call  _ZN1A5funnyEi  // 普通成員函數(shù),實(shí)現(xiàn)簡單  movl  $3, %eax  leave  .cfi_def_cfa 7, 8  ret  .cfi_endproc.LFE5:  .size  main, .-main  .weak  _ZTV3Der  .section  .data.rel.ro.local._ZTV3Der,"awG",@progbits,_ZTV3Der,comdat  .align 8  .type  _ZTV3Der, @object  .size  _ZTV3Der, 24_ZTV3Der:  .quad  0  .quad  _ZTI3Der  .quad  _ZN3Der4fuckEi // Der::fuck(int a);  .weak  _ZTV4Base  .section  .data.rel.ro._ZTV4Base,"awG",@progbits,_ZTV4Base,comdat  .align 8  .type  _ZTV4Base, @object  .size  _ZTV4Base, 24_ZTV4Base:  .quad  0  .quad  _ZTI4Base  .quad  __cxa_pure_virtual // 純虛函數(shù),無對應(yīng)符號表  .weak  _ZTI3Der  .section  .data.rel.ro._ZTI3Der,"awG",@progbits,_ZTI3Der,comdat  .align 8  .type  _ZTI3Der, @object  .size  _ZTI3Der, 24

現(xiàn)在是一個(gè)純虛函數(shù),類中也沒有虛析構(gòu)函數(shù),通過反匯編來看一些這個(gè)實(shí)現(xiàn)。

_ZTV3Der_ZTV4Base 是兩個(gè)虛表,大小為 24, 8 字節(jié)對齊,分別對應(yīng) Der 子類和 Base 父類。虛表中偏移 16 字節(jié)(偏移大小可能和實(shí)現(xiàn)相關(guān))為虛函數(shù)地址,每次構(gòu)造函數(shù)的被調(diào)用的時(shí)候,會(huì)將該偏移地址存儲到父類指針?biāo)趦?nèi)存中,所以在上代碼中看到,在 Base 和 Der 類的構(gòu)函數(shù)中都出現(xiàn)了設(shè)置偏移地址的操作,但是子類構(gòu)造函數(shù)會(huì)覆蓋父類的修改。這樣一來,實(shí)際的函數(shù)運(yùn)行地址依賴構(gòu)造函數(shù),子類對象被構(gòu)造就調(diào)用子類的方法,父類構(gòu)造就調(diào)用父類的方法(非純虛函數(shù)),實(shí)現(xiàn)了運(yùn)行時(shí)多態(tài)。

增加一個(gè)虛函數(shù)后, 后面的虛函數(shù)地址就添加到虛表之中,如下

virtual void Base::shit() {}void Der::shit() override {}_ZTV3Der:  .quad  0  .quad  _ZTI3Der  .quad  _ZN3Der4fuckEi  .quad  _ZN3Der4shitEv  .weak  _ZTV4Base  .section  .data.rel.ro._ZTV4Base,"awG",@progbits,_ZTV4Base,comdat  .align 8  .type  _ZTV4Base, @object  .size  _ZTV4Base, 32_ZTV4Base:  .quad  0  .quad  _ZTI4Base  .quad  __cxa_pure_virtual  .quad  _ZN4Base4shitEv  .weak  _ZTI3Der  .section  .data.rel.ro._ZTI3Der,"awG",@progbits,_ZTI3Der,comdat  .align 8  .type  _ZTI3Der, @object  .size  _ZTI3Der, 24

再調(diào)用另外一個(gè)虛函數(shù)就簡單很多了,直接地址進(jìn)行偏移(這里shit在fuck之后,所以+8)

movq  -8(%rbp), %rax  movq  (%rax), %rax  addq  $8, %rax  movq  (%rax), %rdx  movq  -8(%rbp), %rax  movq  %rax, %rdi  call  *%rdx

簡單畫了一下虛函數(shù)運(yùn)行的內(nèi)存結(jié)構(gòu)圖

關(guān)于如何進(jìn)行C++虛函數(shù)分析問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識。

分享名稱:如何進(jìn)行C++虛函數(shù)分析-創(chuàng)新互聯(lián)
網(wǎng)址分享:http://jinyejixie.com/article34/ccedse.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供微信公眾號、網(wǎng)站設(shè)計(jì)公司、移動(dòng)網(wǎng)站建設(shè)搜索引擎優(yōu)化、網(wǎng)頁設(shè)計(jì)公司網(wǎng)站制作

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

小程序開發(fā)
福建省| 尖扎县| 永嘉县| 怀仁县| 洪湖市| 什邡市| 屯门区| 罗甸县| 凯里市| 彭州市| 闵行区| 西宁市| 垦利县| 三原县| 临武县| 班玛县| 开原市| 大竹县| 江都市| 叶城县| 林甸县| 宣恩县| 荥经县| 台湾省| 临潭县| 旅游| 绥化市| 峨眉山市| 镶黄旗| 台安县| 明水县| 于田县| 锡林浩特市| 阿坝| 偏关县| 鄂伦春自治旗| 连南| 嘉峪关市| 申扎县| 莆田市| 尼勒克县|