全局对象
- 生命周期: main 开始之前构造 ,main结束之后析构
- 因此在main函数之前初始化,构造属于初始化,因此在Inititem(c++) 中构造
- 先调用构造函数,然后再通过atexit函数注册析构函数,在软件退出的时候调用析构函数。
- 在调用析构以前,该全局变量都是有效的。
动态库调试
#include <stdio.h>
#include <string.h>
class CPerson
{
public:
CPerson()
{
m_pszName = new char[100];
strcpy_s(m_pszName, 99, "HELLO");
m_nAge = 0;
printf("%s\n", __FUNCTION__);
}
CPerson(int nSize)
{
m_pszName = new char[nSize];
strcpy_s(m_pszName, nSize - 1, "HELLO");
m_nAge = 0;
printf("%s\n", __FUNCTION__);
}
~CPerson()
{
if (m_pszName != nullptr) {
delete[] m_pszName;
m_pszName = nullptr;
}
printf("%s\n", __FUNCTION__);
}
int GetAge() const {
return m_nAge;
}
void SetAge(int nAge) {
m_nAge = nAge;
}
private:
char* m_pszName;
int m_nAge;
};
CPerson g_pObj;
int main()
{
printf("main end\n");
return 0;
}
构造函数的代码块
析构函数代码块
传递构造函数的参数
构造函数本身是不可以传参数的,所以得生成一个函数,然后靠这个函数传值,这也是全局对象一定会产生构造代理的原因,就算内联还是得产生构造代理。
查看全局对象
#include <stdio.h>
#include <string.h>
class CPerson
{
public:
CPerson()
{
m_pszName = new char[100];
strcpy_s(m_pszName, 99, "HELLO");
m_nAge = 0;
printf("%s\n", __FUNCTION__);
}
CPerson(int nSize)
{
m_pszName = new char[nSize];
strcpy_s(m_pszName, nSize - 1, "HELLO");
m_nAge = 0;
printf("%s\n", __FUNCTION__);
}
~CPerson()
{
if (m_pszName != nullptr) {
delete[] m_pszName;
m_pszName = nullptr;
}
printf("%s\n", __FUNCTION__);
}
int GetAge() const {
return m_nAge;
}
void SetAge(int nAge) {
m_nAge = nAge;
}
private:
char* m_pszName;
int m_nAge;
};
CPerson g_pObj;
int main()
{
printf("Age:%d\r\n", g_pObj.GetAge());
printf("main end\n");
return 0;
}
使用快捷键x
查看指定对象 被哪些函数所调用 快速定位全局对象
还可以使用图形查看
总结
因此识别全局对象只需要看main之前有没有构造代理,构造代理里面可以同时初始化多个全局对象,即多个全局对象可以共用一个全局代理函数,如果类本身没有 构造 和析构函数,可以还原成结构体,因为没有区别
全局对象数组
#include <stdio.h>
#include <string.h>
class CPerson
{
public:
CPerson()
{
m_pszName = new char[100];
strcpy_s(m_pszName, 99, "HELLO");
m_nAge = 0;
printf("%s\n", __FUNCTION__);
}
CPerson(int nSize)
{
m_pszName = new char[nSize];
strcpy_s(m_pszName, nSize - 1, "HELLO");
m_nAge = 0;
printf("%s\n", __FUNCTION__);
}
~CPerson()
{
if (m_pszName != nullptr) {
delete[] m_pszName;
m_pszName = nullptr;
}
printf("%s\n", __FUNCTION__);
}
int GetAge() const {
return m_nAge;
}
void SetAge(int nAge) {
m_nAge = nAge;
}
private:
char* m_pszName;
int m_nAge;
};
CPerson g_pObj;
CPerson g_pObj2[10];
int main()
{
printf("Age:%d\r\n", g_pObj.GetAge());
printf("main end\n");
return 0;
}
全局new对象
#include <stdio.h>
#include <string.h>
class CPerson
{
public:
CPerson()
{
m_pszName = new char[100];
strcpy_s(m_pszName, 99, "HELLO");
m_nAge = 0;
printf("%s\n", __FUNCTION__);
}
CPerson(int nSize)
{
m_pszName = new char[nSize];
strcpy_s(m_pszName, nSize - 1, "HELLO");
m_nAge = 0;
printf("%s\n", __FUNCTION__);
}
~CPerson()
{
if (m_pszName != nullptr) {
delete[] m_pszName;
m_pszName = nullptr;
}
printf("%s\n", __FUNCTION__);
}
int GetAge() const {
return m_nAge;
}
void SetAge(int nAge) {
m_nAge = nAge;
}
private:
char* m_pszName;
int m_nAge;
};
CPerson g_pObj;
CPerson g_pObj2[10];
CPerson* g_pObj3 = new CPerson(10);
int main()
{
printf("Age:%d\r\n", g_pObj.GetAge());
printf("main end\n");
return 0;
}
静态全局对象
静态全局对象和普通全局对象没有区别,只是作用域不同 ,因此静态是无法还原的,他只是在编译的时候做检查。
对于编译器来说,跟普通全局对象是一样的。
#include <stdio.h>
#include <string.h>
class CPerson
{
public:
CPerson()
{
m_pszName = new char[100];
strcpy_s(m_pszName, 99, "HELLO");
m_nAge = 0;
printf("%s\n", __FUNCTION__);
}
CPerson(int nSize)
{
m_pszName = new char[nSize];
strcpy_s(m_pszName, nSize - 1, "HELLO");
m_nAge = 0;
printf("%s\n", __FUNCTION__);
}
~CPerson()
{
if (m_pszName != nullptr) {
delete[] m_pszName;
m_pszName = nullptr;
}
printf("%s\n", __FUNCTION__);
}
int GetAge() const {
return m_nAge;
}
void SetAge(int nAge) {
m_nAge = nAge;
}
private:
char* m_pszName;
int m_nAge;
};
static CPerson g_pObj;
int main()
{
printf("Age:%d\r\n", g_pObj.GetAge());
printf("main end\n");
return 0;
}
main函数
静态局部对象
会在main函数入口 调用构造代理,放全局也没问题(老版本就是),前提是不能有变参
#include <stdio.h>
#include <string.h>
class CPerson
{
public:
CPerson()
{
m_pszName = new char[100];
strcpy_s(m_pszName, 99, "HELLO");
m_nAge = 0;
printf("%s\n", __FUNCTION__);
}
CPerson(int nSize)
{
m_pszName = new char[nSize];
strcpy_s(m_pszName, nSize - 1, "HELLO");
m_nAge = 0;
printf("%s\n", __FUNCTION__);
}
~CPerson()
{
if (m_pszName != nullptr) {
delete[] m_pszName;
m_pszName = nullptr;
}
printf("%s\n", __FUNCTION__);
}
int GetAge() const {
return m_nAge;
}
void SetAge(int nAge) {
m_nAge = nAge;
}
private:
char* m_pszName;
int m_nAge;
};
int main()
{
static CPerson g_pObj;
printf("Age:%d\r\n", g_pObj.GetAge());
printf("main end\n");
return 0;
}
反汇编代码
.text:004010E0 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:004010E0 _main proc near ; CODE XREF: __scrt_common_main_seh(void)+F5↓p
.text:004010E0
.text:004010E0 var_C = dword ptr -0Ch
.text:004010E0 var_4 = dword ptr -4
.text:004010E0 argc = dword ptr 8
.text:004010E0 argv = dword ptr 0Ch
.text:004010E0 envp = dword ptr 10h
.text:004010E0
.text:004010E0 ; FUNCTION CHUNK AT .text:00402200 SIZE 0000000C BYTES
.text:004010E0 ; FUNCTION CHUNK AT .text:00402211 SIZE 0000001D BYTES
.text:004010E0
.text:004010E0 ; __unwind { // unknown_libname_24
.text:004010E0 push ebp
.text:004010E1 mov ebp, esp
.text:004010E3 push 0FFFFFFFFh
.text:004010E5 push offset unknown_libname_24 ; Unnamed sample library
.text:004010EA mov eax, large fs:0
.text:004010F0 push eax
.text:004010F1 mov eax, ___security_cookie
.text:004010F6 xor eax, ebp
.text:004010F8 push eax
.text:004010F9 lea eax, [ebp+var_C]
.text:004010FC mov large fs:0, eax
.text:00401102 mov eax, large fs:2Ch
.text:00401108 mov ecx, TlsIndex
.text:0040110E mov ecx, [eax+ecx*4]
.text:00401111 mov eax, dword_4050B0
.text:00401116 cmp eax, [ecx+4]
.text:0040111C jg short loc_401151
.text:0040111E
.text:0040111E IF_BEGIN: ; CODE XREF: _main+85↓j
.text:0040111E ; _main+BB↓j
.text:0040111E mov ecx, offset g_pObj
.text:00401123 call g_pObj_GetAge__
.text:00401128 push eax ; ArgList
.text:00401129 push offset aAgeD ; "Age:%d\r\n"
.text:0040112E call printf
.text:00401133 push offset aMainEnd ; "main end\n"
.text:00401138 call printf
.text:0040113D add esp, 0Ch
.text:00401140 xor eax, eax
.text:00401142 mov ecx, [ebp+var_C]
.text:00401145 mov large fs:0, ecx
.text:0040114C pop ecx
.text:0040114D mov esp, ebp
.text:0040114F pop ebp
.text:00401150 retn
.text:00401151 ; ---------------------------------------------------------------------------
.text:00401151
.text:00401151 loc_401151: ; CODE XREF: _main+3C↑j
.text:00401151 push offset dword_4050B0
.text:00401156 call __Init_thread_header
.text:0040115B add esp, 4
.text:0040115E cmp dword_4050B0, 0FFFFFFFFh
.text:00401165 jnz short IF_BEGIN ; ;判断是否初数化
.text:00401167 push 8 ; Size
.text:00401169 mov ecx, offset g_pObj ; void *
.text:0040116E ; try {
.text:0040116E mov [ebp+var_4], 0 ; 构造代理
.text:00401175 call memset_0
.text:0040117A mov ecx, offset g_pObj
.text:0040117F call Person_construct
.text:00401184 push offset Person_destructor ; void (__cdecl *)()
.text:00401189 call _atexit
.text:0040118E push offset dword_4050B0
.text:00401193 call __Init_thread_footer
.text:00401198 add esp, 8
.text:0040119B jmp short IF_BEGIN
.text:0040119B ; } // starts at 40116E
.text:0040119B ; } // starts at 4010E0
.text:0040119B _main endp
静态局部变量传变参
此时,构造代理只能放main(),不能放全局,通过标志 $TSS0 来判断是否初始化 ,如果<= 0 就代表已经初始化,如果没初始化,就会调构造代理,如果已经初始化,就不会调用 不会这个
#include <stdio.h>
#include <string.h>
class CPerson
{
public:
CPerson()
{
m_pszName = new char[100];
strcpy_s(m_pszName, 99, "HELLO");
m_nAge = 0;
printf("%s\n", __FUNCTION__);
}
CPerson(int nSize)
{
m_pszName = new char[nSize];
strcpy_s(m_pszName, nSize - 1, "HELLO");
m_nAge = 0;
printf("%s\n", __FUNCTION__);
}
~CPerson()
{
if (m_pszName != nullptr) {
delete[] m_pszName;
m_pszName = nullptr;
}
printf("%s\n", __FUNCTION__);
}
int GetAge() const {
return m_nAge;
}
void SetAge(int nAge) {
m_nAge = nAge;
}
private:
char* m_pszName;
int m_nAge;
};
int main(int argc,char* argv)
{
static CPerson g_pObj(argc);
printf("Age:%d\r\n", g_pObj.GetAge());
printf("main end\n");
return 0;
}
对象作为函数参数识别
传指针很像成员函数
- 函数外构造
- 函数内析构
浅拷贝
没有构造函数
浅拷贝: 抬栈,把对象成员拷贝到栈里面
当成员较少是,直接 把成员push到栈里面
#include <stdio.h>
#include <string.h>
class CPerson
{
public:
~CPerson()
{
if (m_pszName != nullptr) {
delete[] m_pszName;
m_pszName = nullptr;
}
printf("%s\n", __FUNCTION__);
}
int GetAge() const {
return m_nAge;
}
void SetAge(int nAge) {
m_nAge = nAge;
}
const char* GetName() {
return m_pszName;
}
private:
char* m_pszName;
int m_nAge;
};
void showPerson(CPerson Obj)
{
}
int main(int argc)
{
CPerson pObj = {0};
showPerson(pObj); //浅拷贝 memcpy
printf("main end\n");
return 0;
}
main函数反汇编
当类成员很多或者类很大是,就不会push,而用拷贝
#include <stdio.h>
#include <string.h>
class CPerson
{
public:
~CPerson()
{
if (m_pszName != nullptr) {
delete[] m_pszName;
m_pszName = nullptr;
}
printf("%s\n", __FUNCTION__);
}
int GetAge() const {
return m_nAge;
}
void SetAge(int nAge) {
m_nAge = nAge;
}
const char* GetName() {
return m_pszName;
}
private:
char* m_pszName;
int m_nAge;
int m_nMember[100];
};
void showPerson(CPerson Obj)
{
}
int main(int argc)
{
CPerson pObj = {};
showPerson(pObj); //浅拷贝 memcpy
printf("main end\n");
return 0;
}
没有构造和析构
#include <stdio.h>
#include <string.h>
class CPerson
{
public:
int GetAge() const {
return m_nAge;
}
void SetAge(int nAge) {
m_nAge = nAge;
}
const char* GetName() {
return m_pszName;
}
public:
char* m_pszName;
int m_nAge;
int m_nMember[100];
};
void showPerson(CPerson Obj)
{
printf("name:%s\n", Obj.m_pszName);
}
int main(int argc)
{
CPerson pObj = { 0 };
showPerson(pObj);
printf("main end\n");
return 0;
}
main函数反汇编
可以看到此时没有析构,所以无法判断是否为对象,当做结构也是可以的
深拷贝
当一个对象有拷贝构造,那么将不能浅拷贝
把栈顶当this指针,再调拷贝构造,说明参数是对象
当类成员有指针成员的时候,需要采用深拷贝,先new一个,然后将内容拷贝。这样做到的缺点是,效率比较低。
#include <stdio.h>
#include <string.h>
class CPerson
{
public:
CPerson()
{
m_pszName = new char[100];
strcpy_s(m_pszName, 99, "HELLO");
m_nAge = 0;
printf("%s\n", __FUNCTION__);
}
CPerson(int nSize)
{
m_pszName = new char[nSize];
strcpy_s(m_pszName, nSize - 1, "HELLO");
m_nAge = 0;
printf("int nSize %s\n", __FUNCTION__);
}
CPerson(const CPerson& obj)
{
m_pszName = new char[100];
m_nAge = 0;
strcpy_s(m_pszName, 99, obj.m_pszName);
printf("copy %s\n", __FUNCTION__);
}
~CPerson()
{
if (m_pszName != nullptr) {
delete[] m_pszName;
m_pszName = nullptr;
}
printf("%s\n", __FUNCTION__);
}
int GetAge() const {
return m_nAge;
}
void SetAge(int nAge) {
m_nAge = nAge;
}
const char* GetName() {
return m_pszName;
}
public:
char* m_pszName;
int m_nAge;
};
void showPerson(CPerson Obj)
{
printf("name:%s\n", Obj.m_pszName);
}
int main(int argc)
{
CPerson pObj = { 0 };
showPerson(pObj); //深拷贝
printf("main end\n");
return 0;
}
main函数反汇编
抬栈 sub esp, xxx
以esp为this调用拷贝构造函数
有参构造函数
拷贝构造函数
showPerson函数
析构函数
识别特征:
- 函数外构造
- 函数内析构
返回值为局部对象(非指针)
如果没有构造和析构可以当结构体返回
在函数外构造接受的对象,将对象的this指针传递进入函数,使用完后,在函数内部进行构造。
会将返回值的指针传递进去,需要返回指的this指针调拷贝构造。
识别特征:
- 函数内构造
- 函数外析构
#include <stdio.h>
#include <string.h>
class CPerson
{
public:
CPerson()
{
m_pszName = new char[100];
strcpy_s(m_pszName, 99, "HELLO");
m_nAge = 0;
printf("%s\n", __FUNCTION__);
}
CPerson(int nSize)
{
m_pszName = new char[nSize];
strcpy_s(m_pszName, nSize - 1, "HELLO");
m_nAge = 0;
printf("%s\n", __FUNCTION__);
}
CPerson(const CPerson& obj)
{
m_pszName = new char[100];
m_nAge = 0;
strcpy_s(m_pszName, 99, obj.m_pszName);
printf("copy %s\n", __FUNCTION__);
}
~CPerson()
{
if (m_pszName != nullptr) {
delete[] m_pszName;
m_pszName = nullptr;
}
printf("%s\n", __FUNCTION__);
}
int GetAge() const {
return m_nAge;
}
void SetAge(int nAge) {
m_nAge = nAge;
}
const char* GetName() {
return m_pszName;
}
public:
char* m_pszName;
int m_nAge;
};
//此处debug版和release版有区别:应该是 先构造 obj 在用obj取构造返回对象 而 obj 的this是从参数传过来的
//因此应该是 先构造 obj 在拷贝构造参数 release直接优成了 构造 返回值对象
CPerson getPerson()
{
CPerson obj;
return obj;
}
int main(int argc)
{
printf("age:%d\n", getPerson().GetAge());
return 0;
}
main函数反汇编代码
getPerson函数
构造函数
Debug版本
main函数反汇编
getPerson函数
析构函数
拷贝构造函数
析构函数
无名对象
无名对象会被优掉
不会调用拷贝构造,直接构造对象
// C++RE.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <stdio.h>
#include <string.h>
class CPerson
{
public:
CPerson()
{
m_pszName = new char[100];
strcpy_s(m_pszName, 99, "HELLO");
m_nAge = 0;
printf("%s\n", __FUNCTION__);
}
CPerson(int nSize)
{
m_pszName = new char[nSize];
strcpy_s(m_pszName, nSize - 1, "HELLO");
m_nAge = 0;
printf("%s\n", __FUNCTION__);
}
CPerson(const CPerson& obj)
{
m_pszName = new char[100];
m_nAge = 0;
strcpy_s(m_pszName, 99, obj.m_pszName);
printf("copy %s\n", __FUNCTION__);
}
~CPerson()
{
if (m_pszName != nullptr) {
delete[] m_pszName;
m_pszName = nullptr;
}
printf("%s\n", __FUNCTION__);
}
int GetAge() const {
return m_nAge;
}
void SetAge(int nAge) {
m_nAge = nAge;
}
const char* GetName() {
return m_pszName;
}
public:
char* m_pszName;
int m_nAge;
};
CPerson getPerson()
{
return CPerson(); //无名对象
}
int main(int argc)
{
printf("age:%d\n", getPerson().GetAge());
return 0;
}