欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++從匯編的視角審視對象的創(chuàng)建問題

 更新時間:2022年01月21日 09:20:06   作者:楷哥  
這篇文章主要介紹了C++從匯編的視角看對象的創(chuàng)建,從匯編的視角來看,調用構造器和調用 “返回對象” 的函數(shù)是一樣的,從匯編的角度來看,對象就是一堆數(shù)據(jù)的排列,比如說最普通的對象就是數(shù)據(jù)成員按照聲明順序直接排列,需要的朋友可以參考下

前言

很久以前閱讀了 CSAPP 這本書,可惜看過的東西基本都忘記了,只知道一些工具可以幫助我分析。今天突然對 “返回對象的函數(shù)” 很感興趣,于是分析了一下匯編。

返回對象的函數(shù)如下,現(xiàn)在有一種叫做 NRV 優(yōu)化的技術可以避免低效率,如果把它關掉,它將會執(zhí)行以下過程:創(chuàng)建一個對象 apple,然后返回一個 apple,發(fā)生一次拷貝構造函數(shù),所以存在執(zhí)行效率問題;如果你還寫了 Apple a = GetApple();,那么還將會發(fā)生一次賦值拷貝構造。

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;
}

構造函數(shù)的執(zhí)行過程分析

構造函數(shù)的執(zhí)行過程:即使是無參構造函數(shù),調用之前仍然要傳參。傳入的是一個地址,需要在函數(shù)運行結束的時候,這個地址上有了一個對象。

main 函數(shù)如下:

int main() {
  Apple a;
  int x = a.a;
  x = 10;
  return 0;
}

生成的匯編代碼:

# main 函數(shù)構造對象的地方:
	pushq	%rbp              # rbp 壓棧
	movq	%rsp, %rbp        # rsp 替換 rbp
	pushq	%rbx              # rbx 壓棧,保存現(xiàn)場
	subq	$24, %rsp         # rsp 自減 24,棧向下增長,相當于擴展 24 字節(jié)
	leaq	-24(%rbp), %rax   # 將地址賦值給 rax,rax 是 Apple 對象開始的地方
	movq	%rax, %rdi        # rdi 傳參
	call	_ZN5AppleC1Ev
	movl	-28(%rbp), %eax   # 返回之后,會執(zhí)行 int x = a.a;
	movl	%eax, -20(%rbp)   # 取第一個字節(jié)賦值給 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 字節(jié)為 1
	movq	-8(%rbp), %rax    # rax 其實還是一樣的
	movl	$2, 4(%rax)       # 變址尋址,其實就是間接尋址加一個偏移量,設置 4 字節(jié)為 2
	nop
	popq	%rbp
	ret                       # 于是最終 rdi 開始,向下的 8 字節(jié)是 Apple 對象

返回對象函數(shù)的分析

從匯編的視角來看,調用構造器和調用 “返回對象” 的函數(shù)是一樣的。它的執(zhí)行過程是:即使是兩個函數(shù)都是無參的,它仍然會進行一次傳參。傳入的是一個地址,需要在函數(shù)調用結束后,這個地址上有了一個對象。從匯編的角度來看,對象就是一堆數(shù)據(jù)的排列,比如說最普通的對象就是數(shù)據(jù)成員按照聲明順序直接排列。

舉個實際點的例子。Apple 這個類,有兩個成員,a 和 b。調用了構造器或者 “返回對象” 的函數(shù),先傳入一個地址,之后函數(shù)里面會在這個地址上存放兩個數(shù),分別是 a 和 b 的值,然后返回。此時,這個地址上就有了 a 和 b,雖然機器看不到對象,但是對我們來說,它就是一個對象。

如果關閉了 NRV 優(yōu)化,那么首先會傳入一個地址,然后構造一個對象,這個對象將會被拷貝構造到傳入的地址上,返回。之后如果調用了一次賦值,那么還要將這個地址上的對象復制構造給棧上的變量。整個過程,實際上存在兩個臨時對象,發(fā)生了一次構造、一次復制構造、一次賦值構造。

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 已經(jīng)存放對象,-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 函數(shù)的匯編代碼
	pushq	%rbp
	movq	%rsp, %rbp
	subq	$32, %rsp
	movq	%rdi, -24(%rbp)    # 分配空間,然后將傳過來的 rdi 放進去
	leaq	-8(%rbp), %rax     
	movq	%rax, %rdi         # 前面分析過了,調用回來時 rax 開始的 8 字節(jié)就是對象
	call	_ZN5AppleC1Ev
	leaq	-8(%rbp), %rdx     # 取 rax 地址到 rdx
	movq	-24(%rbp), %rax    # 取 rdi 地址到 rax
	movq	%rdx, %rsi         # rsi 已經(jīng)有對象了
	movq	%rax, %rdi         # rsi 和 rdi 都用來傳參          
	call	_ZN5AppleC1ERKS_   # 調用拷貝構造函數(shù)
	leaq	-8(%rbp), %rax     # rax 上有對象了
	movq	%rax, %rdi         # rdi 傳參,準備調用析構函數(shù)
	call	_ZN5AppleD1Ev
	nop
	movq	-24(%rbp), %rax    # 前面調用拷貝構造前的地址,這個地址有對象了
	leave
	ret

# 拷貝構造函數(shù)
	pushq	%rbp
	movq	%rsp, %rbp
	movq	%rdi, -8(%rbp)    # rdi 無對象
	movq	%rsi, -16(%rbp)   # rsi 有對象
	movq	-16(%rbp), %rax   # 把對象放到 rax
	movl	(%rax), %edx      # 把第一屬性(4 字節(jié))移動到 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

到此這篇關于C++:從匯編的視角看對象的創(chuàng)建的文章就介紹到這了,更多相關C++匯編對象內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • c++ *運算符重載

    c++ *運算符重載

    運算符重載重載運算符是C++ 的一個重要特性,使用運算符重載, 的一個重要特性,使用運算符重載, 重載運算符是程序員可以把C++ 運算符的定義擴展到運算分量是對象
    2014-09-09
  • C++產(chǎn)生隨機數(shù)的實現(xiàn)代碼

    C++產(chǎn)生隨機數(shù)的實現(xiàn)代碼

    本篇文章是對C++中產(chǎn)生隨機數(shù)的實現(xiàn)代碼進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • 詳解C++語言中std::array的神奇用法

    詳解C++語言中std::array的神奇用法

    本文的代碼都在C++17環(huán)境下編譯運行。當前主流的g++版本已經(jīng)能支持C++17標準,但是很多版本(如gcc 7.3)的C++17特性不是默認打開的,需要手工添加編譯選項-std=c++17,具體內容詳情跟隨小編一起學習吧
    2021-05-05
  • C++ deque與vector對比的優(yōu)缺點

    C++ deque與vector對比的優(yōu)缺點

    這篇文章主要介紹了C++中deque與vector相比的優(yōu)勢與劣勢,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧
    2023-01-01
  • c++隱式類型轉換存在的問題解析

    c++隱式類型轉換存在的問題解析

    隱式轉換,是指不需要用戶干預,編譯器私下進行的類型轉換行為,很多時候用戶都不知道具體進行了哪些轉換,這篇文章主要介紹了c++隱式類型轉換存在的陷阱,需要的朋友可以參考下
    2022-03-03
  • C語言智能指針之weak_ptr淺析

    C語言智能指針之weak_ptr淺析

    這篇文章主要介紹了 C++11智能指針之weak_ptr詳解,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-10-10
  • C++實現(xiàn)簡單迷宮游戲

    C++實現(xiàn)簡單迷宮游戲

    這篇文章主要為大家詳細介紹了C++實現(xiàn)簡單迷宮游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-01-01
  • C++ opencv ffmpeg圖片序列化實現(xiàn)代碼解析

    C++ opencv ffmpeg圖片序列化實現(xiàn)代碼解析

    這篇文章主要介紹了C++ opencv ffmpeg圖片序列化實現(xiàn)代碼解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-08-08
  • 用C語言實現(xiàn)推箱子游戲實例

    用C語言實現(xiàn)推箱子游戲實例

    大家好,本篇文章主要講的是用C語言實現(xiàn)推箱子游戲實例,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-01-01
  • C語言中 & 和 &&的區(qū)別詳解

    C語言中 & 和 &&的區(qū)別詳解

    這篇文章主要介紹了C語言中 & 和 &&的區(qū)別詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-01-01

最新評論