這篇文章給大家分享的是有關(guān)C++中虛函數(shù)怎么用的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過來看看吧。
公司主營業(yè)務(wù):做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)、移動(dòng)網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。成都創(chuàng)新互聯(lián)是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來驚喜。成都創(chuàng)新互聯(lián)推出鹽池免費(fèi)做網(wǎng)站回饋大家。虛函數(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, @function main: .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ì)將該偏移地址存儲(chǔ)到父類指針?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)于“C++中虛函數(shù)怎么用”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)建站jinyejixie.com,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。
本文名稱:C++中虛函數(shù)怎么用-創(chuàng)新互聯(lián)
分享鏈接:http://jinyejixie.com/article20/dieijo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供App設(shè)計(jì)、App開發(fā)、做網(wǎng)站、品牌網(wǎng)站制作、網(wǎng)站導(dǎo)航、ChatGPT
聲明:本網(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)
猜你還喜歡下面的內(nèi)容