构造。析构函数的出现时机
对象生成时会自动调用构造函数,但由于不同作用域的对象生命周期不同,构造函数出现的时机也不同,那么只要知道了对象的生命周期,便可以推断出构造函数的调用时机。
局部对象
当对象产生时,便可能引发构造函数,一般来说进入对象的作用域时,编译器会产生构造函数的代码,由于构造函数属于成员函数,因此在调用过程中需要传递this指针,构造函数调用结束后,会返回this指针,这个返回的this指针就是构造函数的一个特征,所以局部对象的构造满足两个条件:
- 该成员函数是这个对象在作用域调用的第一个成员函数
- 这个函数返回this指针
出作用域时调用析构函数,同样要传递this指针.
堆对象
堆重要的是识别申请和使用,申请用到malloc函数,malloc只负责申请空间,不会调用构造函数,而new则是封装了malloc和调用构造的动作。而识别构造的中点在于new产生的汇编代码会有明显特征:
je
........ ;构造,或构造的内联
jmp
堆对象的析构要使用者主动使用delete
参数对象
当对象作为参数的时候,会调用拷贝构造,拷贝构造的参数为对象的引用,对象作为参数的时候,会出发拷贝构造。
返回对象
对象时使用到拷贝构造
-
临时对象:用来说明作用域,遇到分号就析构了
pTest = &GetObj(); // 产生临时对象 CTest t3 = GetObj(); // 不产生临时对象, 算拷贝构造
-
无名对象:作用域跟着引用的作用域
CTest& refTest = GetObj() //这里产生无名对象
全局对象
几个代理函数:
$E4: 为了统一函数指针,方便_initterm中循环调用函数指针
-
$E1:负责传递采纳数,获得返回值并处理
构造函数的$E1有返回值但用户无权调度
$E3:调用atexit注册$E2
-
$E2:析构代理,调用析构函数,因为atexit是c约定,析构是thiscall,只能在种种包一层调用
想要找全局对象,只需要在ida里找到那里引用的了atexit函数就可以找到所有全局对象(正常情况)
静态对象
关键看标志,标志判断的地方会有一个跳转,跳过的代码就是注册析构
全局和静态对象析构的过程都在出main函数后有exit内的doexit来反向循环调用构造时注册的析构代理函数,先构造的后调用
对象数组
CTest *aryObj = new CTest[8];
在对象数组的前4个字节放元素个数,为的是给delete准备
在delete的时候会push一个数字,这个数字用位来表示作用
第0位:是否释放空间
第1位: 是否是对象数组