網站首頁 編程語言 正文
前言
很久以前閱讀了 CSAPP 這本書,可惜看過的東西基本都忘記了,只知道一些工具可以幫助我分析。今天突然對 “返回對象的函數” 很感興趣,于是分析了一下匯編。
返回對象的函數如下,現在有一種叫做 NRV 優化的技術可以避免低效率,如果把它關掉,它將會執行以下過程:創建一個對象 apple,然后返回一個 apple,發生一次拷貝構造函數,所以存在執行效率問題;如果你還寫了 Apple a = GetApple();
,那么還將會發生一次賦值拷貝構造。
Apple GetApple() { Apple apple{}; return apple; }
代碼:
class Apple { public: Apple() { } ~Apple() { } Apple(const Apple& apple) { this->a = apple.a; this->b = apple.b; } Apple& operator=(const Apple& apple) { this->a = apple.a; this->b = apple.b; return *this; } void Print() { cout << a << " " << b << endl; } int a = 1; int b = 2; }; Apple GetApple() { Apple apple{}; return apple; }
構造函數的執行過程分析
構造函數的執行過程:即使是無參構造函數,調用之前仍然要傳參。傳入的是一個地址,需要在函數運行結束的時候,這個地址上有了一個對象。
main 函數如下:
int main() { Apple a; int x = a.a; x = 10; return 0; }
生成的匯編代碼:
# main 函數構造對象的地方: pushq %rbp # rbp 壓棧 movq %rsp, %rbp # rsp 替換 rbp pushq %rbx # rbx 壓棧,保存現場 subq $24, %rsp # rsp 自減 24,棧向下增長,相當于擴展 24 字節 leaq -24(%rbp), %rax # 將地址賦值給 rax,rax 是 Apple 對象開始的地方 movq %rax, %rdi # rdi 傳參 call _ZN5AppleC1Ev movl -28(%rbp), %eax # 返回之后,會執行 int x = a.a; movl %eax, -20(%rbp) # 取第一個字節賦值給 x movl $10, -20(%rbp) # 直接使用 10 賦值給 x;寫著行的目的只是為了確定 x 的位置 # 默認構造器: pushq %rbp # rbp 壓棧 movq %rsp, %rbp # rsp 替換 rbp movq %rdi, -8(%rbp) # rdi 是前面 rax 的值 movq -8(%rbp), %rax # 設置 rax 為 rbp 減 8 movl $1, (%rax) # 間接尋址,設置 4 字節為 1 movq -8(%rbp), %rax # rax 其實還是一樣的 movl $2, 4(%rax) # 變址尋址,其實就是間接尋址加一個偏移量,設置 4 字節為 2 nop popq %rbp ret # 于是最終 rdi 開始,向下的 8 字節是 Apple 對象
返回對象函數的分析
從匯編的視角來看,調用構造器和調用 “返回對象” 的函數是一樣的。它的執行過程是:即使是兩個函數都是無參的,它仍然會進行一次傳參。傳入的是一個地址,需要在函數調用結束后,這個地址上有了一個對象。從匯編的角度來看,對象就是一堆數據的排列,比如說最普通的對象就是數據成員按照聲明順序直接排列。
舉個實際點的例子。Apple 這個類,有兩個成員,a 和 b。調用了構造器或者 “返回對象” 的函數,先傳入一個地址,之后函數里面會在這個地址上存放兩個數,分別是 a 和 b 的值,然后返回。此時,這個地址上就有了 a 和 b,雖然機器看不到對象,但是對我們來說,它就是一個對象。
如果關閉了 NRV 優化,那么首先會傳入一個地址,然后構造一個對象,這個對象將會被拷貝構造到傳入的地址上,返回。之后如果調用了一次賦值,那么還要將這個地址上的對象復制構造給棧上的變量。整個過程,實際上存在兩個臨時對象,發生了一次構造、一次復制構造、一次賦值構造。
main: .LFB3535: pushq %rbp movq %rsp, %rbp pushq %rbx subq $40, %rsp leaq -36(%rbp), %rax movq %rax, %rdi call _ZN5AppleC1Ev movl -36(%rbp), %eax movl %eax, -20(%rbp) movl $10, -20(%rbp) leaq -28(%rbp), %rax # 這里之前的匯編是一樣的,調用 GetApple movq %rax, %rdi # rdi,傳參 call _Z8GetApplev leaq -28(%rbp), %rdx # rax 已經存放對象,-28 的位置有對象 leaq -36(%rbp), %rax movq %rdx, %rsi movq %rax, %rdi call _ZN5AppleaSERKS_ leaq -28(%rbp), %rax movq %rax, %rdi call _ZN5AppleD1Ev movl $0, %ebx leaq -36(%rbp), %rax movq %rax, %rdi call _ZN5AppleD1Ev movl %ebx, %eax addq $40, %rsp popq %rbx popq %rbp # GetApple 函數的匯編代碼 pushq %rbp movq %rsp, %rbp subq $32, %rsp movq %rdi, -24(%rbp) # 分配空間,然后將傳過來的 rdi 放進去 leaq -8(%rbp), %rax movq %rax, %rdi # 前面分析過了,調用回來時 rax 開始的 8 字節就是對象 call _ZN5AppleC1Ev leaq -8(%rbp), %rdx # 取 rax 地址到 rdx movq -24(%rbp), %rax # 取 rdi 地址到 rax movq %rdx, %rsi # rsi 已經有對象了 movq %rax, %rdi # rsi 和 rdi 都用來傳參 call _ZN5AppleC1ERKS_ # 調用拷貝構造函數 leaq -8(%rbp), %rax # rax 上有對象了 movq %rax, %rdi # rdi 傳參,準備調用析構函數 call _ZN5AppleD1Ev nop movq -24(%rbp), %rax # 前面調用拷貝構造前的地址,這個地址有對象了 leave ret # 拷貝構造函數 pushq %rbp movq %rsp, %rbp movq %rdi, -8(%rbp) # rdi 無對象 movq %rsi, -16(%rbp) # rsi 有對象 movq -16(%rbp), %rax # 把對象放到 rax movl (%rax), %edx # 把第一屬性(4 字節)移動到 edx movq -8(%rbp), %rax movl %edx, (%rax) # 把第一屬性移動到 rdi movq -16(%rbp), %rax movl 4(%rax), %edx # 把第二屬性移動到 edx movq -8(%rbp), %rax movl %edx, 4(%rax) # 把第二屬性移動到 rdi 上 movq -8(%rbp), %rax # rdi 上有了對象,rax 也設置一個 popq %rbp ret
原文鏈接:https://www.cnblogs.com/zzk0/p/15827536.html
相關推薦
- 2022-06-14 利用Python?NumPy庫及Matplotlib庫繪制數學函數圖像_python
- 2022-10-29 C++ 函數模板的重載與實參推斷
- 2024-03-13 QAobject修改excel字體亂碼問題
- 2023-12-14 idea配置tomcat熱部署
- 2022-05-15 實例詳解Python的進程,線程和協程_python
- 2022-01-04 給所有使用el-table組件特定列添加統一事件及樣式
- 2022-04-07 一篇文章帶你學習Python3的高級特性(2)_python
- 2022-11-17 Android形狀圖形與狀態列表圖形及九宮格圖片超詳細講解_Android
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支