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 是通过复制构造函数进行初始化的。复制构造函数初始化要求左值是一个已有对象,而非创建好了的对象,因此此时不需要创建一个临时对象