zoukankan      html  css  js  c++  java
  • 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
    ", sizeof(test), test.getNum());
    
        std::cout << "Hello World!
    "; 
    }

    输出: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
    "
    .text:00412804                 call    sub_411055
    .text:00412809                 add     esp, 0Ch
    .text:0041280C                 push    offset Str      ; "Hello World!
    "
    .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 是通过复制构造函数进行初始化的。复制构造函数初始化要求左值是一个已有对象,而非创建好了的对象,因此此时不需要创建一个临时对象

  • 相关阅读:
    初始化块:
    继承与组合:
    instanceof用法及本质:
    引用变量的强制类型转换:
    多态是如何产生的:
    program by the way......
    多层次继承,构造器之间的调用顺序!
    调用父类构造器:super
    子类覆写的变量被private隐藏,强制转换方式通过子类访问父类的被覆写变量:
    super限定,子类中系统查找变量的顺序:
  • 原文地址:https://www.cnblogs.com/xiangtingshen/p/11400070.html
Copyright © 2011-2022 走看看