C++對象內存布局,this指針,對象作為參數,作為返回值


 

class TestClass
{
public:
    void setNum(int num)
    {
        m_num1 = num;
    }
    int getNum()
    {
        return m_num1;
    }
private:
    int m_num1;
    int m_num2;
};

 

#include "pch.h"
#include <iostream>
#include "mytest.h"

int main()
{
    TestClass test;
    test.setNum(100);

    printf("sizeof testClass=%d,num = %d\n", sizeof(test), test.getNum());

    std::cout << "Hello World!\n"; 
}

輸出:sizeof testClass=8,num = 100

 

沒有虛函數時,test變量在內存中的分布

 

若存在虛函數

class TestClass
{
public:
    virtual void setNum(int num)
    {
        m_num1 = num;
    }
    virtual int getNum()
    {
        return m_num1;
    }
private:
    int m_num1;
    int m_num2;
};

 

 

 

我們用IDA打開看一下反匯編

ext:004127B0 var_D8          = byte ptr -0D8h
.text:004127B0 var_14          = byte ptr -14h
.text:004127B0 var_4           = dword ptr -4
.text:004127B0
.text:004127B0                 push    ebp
.text:004127B1                 mov     ebp, esp
.text:004127B3                 sub     esp, 0D8h
.text:004127B9                 push    ebx
.text:004127BA                 push    esi
.text:004127BB                 push    edi
.text:004127BC                 lea     edi, [ebp+var_D8]
.text:004127C2                 mov     ecx, 36h
.text:004127C7                 mov     eax, 0CCCCCCCCh
.text:004127CC                 rep stosd
.text:004127CE                 mov     eax, ___security_cookie
.text:004127D3                 xor     eax, ebp
.text:004127D5                 mov     [ebp+var_4], eax
.text:004127D8                 mov     ecx, offset unk_41E009
.text:004127DD                 call    sub_411299
.text:004127E2                 lea ecx, [ebp+var_14] // ecx 保存this指針,通過this指針地址偏移來調用類成員
.text:004127E5                 call    sub_411311
.text:004127EA                 push    100
.text:004127EC                 lea ecx, [ebp+var_14]
.text:004127EF                 call    sub_41113B
.text:004127F4                 lea ecx, [ebp+var_14]
.text:004127F7                 call    sub_4112A8
.text:004127FC                 push    eax
.text:004127FD                 push    0Ch
.text:004127FF                 push    offset aSizeofTestclas ; "sizeof testClass=%d,num = %d\n"
.text:00412804                 call    sub_411055
.text:00412809                 add     esp, 0Ch
.text:0041280C                 push    offset Str      ; "Hello World!\n"
.text:00412811                 mov     eax, ds:?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A ; std::basic_ostream<char,std::char_traits<char>> std::cout
.text:00412816                 push    eax             ; int
.text:00412817                 call    sub_411226

 

對象作為返回值
1、分配一個臨時對象空間 main_object;
2、把臨時對象入棧
3、調用返回局部對象的函數

在返回局部對象的函數里面
fun_object;
一些局部變量操作
返回的時候用用局部對象作為參數,傳入main_object對象的this指針
調用復制構造函數
用EAX返回this指針

tt=main_object;

 

一下轉自:https://blog.csdn.net/qq_22660775/article/details/89854545

C++規定當函數返回的是非引用類型時,函數會創建臨時對象(temporary object),函數返回的就是這個臨時對象。在求解表達式時,如果需要一個地方存儲其運算結果,編譯器會創建一個沒有命名的對象,這就是臨時對象。淺顯的說,當你調用了函數,函數會 return一個值 那么這個值總得有存放的地方吧,編譯器就把會把值存放在一個沒有命名法臨時對象中。

我們舉個例子來說明一下,首先定義一個類:

class B { public: B(){ cout << "B的構造函數" << endl; } B(int i){ cout << "帶int型參數的B的構造函數" << endl; } B(const B &ano){ cout << "B的復制構造函數" << endl; } B& operator=(const B& rhs){ cout << "B的賦值操作符" << endl; return *this; } virtual ~B(){ cout << "B的析構函數" << endl; } }; 

 

定義一個函數:

B func2() { B b; return b; }

上面的函數返回一個非引用類型的變量,我們寫兩個測試函數,來看看返回一個非引用類型的變量會發生什么

void test1() { B t; t = func2(); } void test2() { B t = func2(); }

運行測試函數test1(),其運行結果為:

B的構造函數 //構造主方法內的對象t
B的構造函數 //構造fun2內的局部對象b
B的復制構造函數 //將func2的局部對象復制到一個臨時對象
B的析構函數 //析構局部對象b
B的賦值操作符 //使用臨時對象初始化t
B的析構函數 //析構臨時對象
B的析構函數 //析構對象t

由於 t 的初始化采用的是operator=操作符,operator=要求必須使用一個已經創建好了的對象對左值進行復制,所以此時必須先形成一個臨時對象,然后將臨時對象賦值給 t

運行測試函數test2(),其運行結果為:

B的構造函數 //構造fun2內的局部對象b  
B的復制構造函數   
B的析構函數
B的析構函數

由於 t 是通過復制構造函數進行初始化的。復制構造函數初始化要求左值是一個已有對象,而非創建好了的對象,因此此時不需要創建一個臨時對象

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM