本文用到的反匯編工具是objconv,使用方法可以看我另一篇文章https://blog.csdn.net/weixin_45001971/article/details/128660642。
成都創新互聯公司自2013年創立以來,是專業互聯網技術服務公司,擁有項目成都網站設計、成都做網站、外貿網站建設網站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元府谷做網站,已為上家服務,為府谷各地企業和個人服務,聯系電話:18980820575其它文章:
從匯編的角度了解C++原理——類的儲存結構和函數調用
從匯編的角度了解C++原理——new和malloc的區別
從匯編的角度了解C++原理——虛函數
反匯編。
main:
sub rsp, 56
lea rcx, [rsp+20H]
call ??0A@@QEAA@XZ //調用構造函數
mov eax, 4294967295
add rsp, 56
ret
??0A@@QEAA@XZ: //調用A類的構造函數
mov qword [rsp+8H], rcx
mov rax, qword [rsp+8H]
lea rcx, [rel ??_7A@@6B@] //獲取虛表??_7A@@6B@的地址
mov qword [rax], rcx //把虛表地址放在對象的頭部
mov rax, qword [rsp+8H]
mov dword [rax+8H], 10 //在對象首地址偏移8個字節的位置定義d1變量
mov rax, qword [rsp+8H]
ret
??_7A@@6B@: //A類虛表
dq ?func2@A@@UEAAXXZ //虛函數func2
以上例的匯編代碼可以得出帶虛函數的類的儲存結構如下圖所示。
帶有虛函數的對象的頭部會放置8個字節大小的虛表地址,有了虛表之后的對象會以8個字節為單位去對齊,如上例中的A類,如果沒有虛函數,它的大小為4個字節,而加了虛函數之后,大小變為了16個字節。
在代碼中添加A的子類B,重寫func2方法。
反匯編
main:
sub rsp, 56
lea rcx, [rsp+20H]
call ??0B@@QEAA@XZ //調用B類構造
mov eax, 4294967295
add rsp, 56
ret
??0A@@QEAA@XZ: //A類構造函數
mov qword [rsp+8H], rcx
mov rax, qword [rsp+8H]
lea rcx, [rel ??_7A@@6B@] //把A類虛表的地址放在頭部
mov qword [rax], rcx
mov rax, qword [rsp+8H]
mov dword [rax+8H], 10
mov rax, qword [rsp+8H]
ret
??0B@@QEAA@XZ: //B類構造函數
mov qword [rsp+8H], rcx
sub rsp, 40
mov rcx, qword [rsp+30H]
call ??0A@@QEAA@XZ //調用A類構造
mov rax, qword [rsp+30H]
lea rcx, [rel ??_7B@@6B@] //把B類虛表的地址放在頭部
mov qword [rax], rcx
mov rax, qword [rsp+30H]
add rsp, 40
ret
??_7A@@6B@: //A類虛表
dq ?func2@A@@UEAAXXZ //A::func2
dq ?func3@A@@UEAAXXZ //A::func3
??_7B@@6B@: //B類虛表
dq ?func2@B@@UEAAXXZ //B::func2,被替換為了B實現的func2
dq ?func3@A@@UEAAXXZ //A::func3
從該例中我們可以看到,父類有虛函數時,不光它自己有一張虛表,它的子子孫孫都會各帶有一個自己的虛表,子類重寫虛函數時,會把子類實現的函數指針替換上虛表,把原先父類的函數指針覆蓋掉。
1.3、在棧上調用虛函數在main里添加方法的調用。
反匯編。
main:
sub rsp, 56
lea rcx, [rsp+20H]
call ??0B@@QEAA@XZ
lea rcx, [rsp+20H]
call ?func1@A@@QEAAXXZ //調用A::func1
lea rcx, [rsp+20H]
call ?func2@B@@UEAAXXZ //調用B::func2
lea rcx, [rsp+20H]
call ?func3@A@@UEAAXXZ //調用A::func3
mov eax, 4294967295
add rsp, 56
ret
在棧上調用方法時,因為類型是確定的,所以編譯器在編譯階段就會找到對應的函數去調用,調用過程與普通方法一樣。
1.4、在堆上調用虛函數(通過指針調用,多態)修改例程如下。
反匯編
main:
sub rsp, 72
mov ecx, 16
call ??2@YAPEAX_K@Z
mov qword [rsp+28H], rax
cmp qword [rsp+28H], 0
jz ?_001
mov rcx, qword [rsp+28H] //定義指針b
call ??0B@@QEAA@XZ
mov qword [rsp+30H], rax //rsp+30H指向對象
jmp ?_002 //跳到?_002
?_001: mov qword [rsp+30H], 0
?_002: mov rax, qword [rsp+30H]
mov qword [rsp+38H], rax //rsp+38H指向對象
mov rax, qword [rsp+38H] //rax指向對象
mov qword [rsp+20H], rax //rsp+20H指向對象
mov rcx, qword [rsp+20H] //rcx指向對象
call ?func1@A@@QEAAXXZ //調用A::func1
mov rax, qword [rsp+20H]
mov rax, qword [rax] //取虛表
mov rcx, qword [rsp+20H]
call near [rax] //執行虛表第一個函數,即B::func2
mov rax, qword [rsp+20H]
mov rax, qword [rax]
mov rcx, qword [rsp+20H]
call near [rax+8H] //執行虛表第二個函數,即A::func3
mov eax, 4294967295
add rsp, 72
ret
??_7B@@6B@:
dq ?func2@B@@UEAAXXZ
dq ?func3@A@@UEAAXXZ
從該例可以看到,通過指針來調用函數時。
如果是普通函數,編譯器會直接根據指針類型,找到對應的的方法,而不是根據對象本身的類型,如本例中B類也實現了func1方法,但通過A類指針調用時,寫到匯編里的時A::func1。
如果是虛函數,編譯器不會根據名字來查找函數,而是讓匯編代碼通過虛表中的偏移量來調用,如本例中,b指針執行了func2和func3,這兩個函數都沒有被直接調用,而是以“call near [rax + 偏移量]”的形式調用了,這也是C++中父類指針指向子類對象的多態的實現原理。
你是否還在尋找穩定的海外服務器提供商?創新互聯www.cdcxhl.cn海外機房具備T級流量清洗系統配攻擊溯源,準確流量調度確保服務器高可用性,企業級服務器適合批量采購,新人活動首月15元起,快前往官網查看詳情吧
分享文章:從匯編的角度了解C++原理——虛函數-創新互聯
分享路徑:http://m.kartarina.com/article42/cdcoec.html
成都網站建設公司_創新互聯,為您提供定制網站、營銷型網站建設、App開發、服務器托管、用戶體驗、品牌網站設計
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯