C++RE02-全局类对象

全局对象

  1. 生命周期: main 开始之前构造 ,main结束之后析构
  2. 因此在main函数之前初始化,构造属于初始化,因此在Inititem(c++) 中构造
  3. 先调用构造函数,然后再通过atexit函数注册析构函数,在软件退出的时候调用析构函数。
  4. 在调用析构以前,该全局变量都是有效的。

动态库调试

image.png
image.png

#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;
}

image.png

image.png
构造函数的代码块
image.png

析构函数代码块
image.png

传递构造函数的参数

构造函数本身是不可以传参数的,所以得生成一个函数,然后靠这个函数传值,这也是全局对象一定会产生构造代理的原因,就算内联还是得产生构造代理。

查看全局对象

#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;
}

image.png

使用快捷键x
查看指定对象 被哪些函数所调用 快速定位全局对象
image.png
还可以使用图形查看
image.png
image.png

总结

因此识别全局对象只需要看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;
}

image.png
image.png
image.png

全局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;
}

image.png
image.png

静态全局对象

静态全局对象和普通全局对象没有区别,只是作用域不同 ,因此静态是无法还原的,他只是在编译的时候做检查。
对于编译器来说,跟普通全局对象是一样的。


#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函数
image.png
image.png

静态局部对象

会在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

image.png

静态局部变量传变参

此时,构造代理只能放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;
}

对象作为函数参数识别

传指针很像成员函数

  1. 函数外构造
  2. 函数内析构

浅拷贝

没有构造函数

浅拷贝: 抬栈,把对象成员拷贝到栈里面
当成员较少是,直接 把成员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函数反汇编
image.png
image.png
当类成员很多或者类很大是,就不会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;
}

image.png

没有构造和析构
#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函数反汇编
image.png
可以看到此时没有析构,所以无法判断是否为对象,当做结构也是可以的
image.png

深拷贝

当一个对象有拷贝构造,那么将不能浅拷贝
把栈顶当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调用拷贝构造函数
image.png

有参构造函数

image.png

拷贝构造函数

image.png

showPerson函数

image.png

析构函数

image.png

识别特征:
  • 函数外构造
  • 函数内析构

返回值为局部对象(非指针)

如果没有构造和析构可以当结构体返回
在函数外构造接受的对象,将对象的this指针传递进入函数,使用完后,在函数内部进行构造。
会将返回值的指针传递进去,需要返回指的this指针调拷贝构造。
识别特征:

  1. 函数内构造
  2. 函数外析构
#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函数反汇编代码

image.png

getPerson函数

image.png

构造函数

image.png

Debug版本

main函数反汇编

image.png

getPerson函数

image.png

析构函数

image.png

拷贝构造函数

image.png

析构函数

image.png

无名对象

无名对象会被优掉
不会调用拷贝构造,直接构造对象

// 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;
}

image.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黑桃鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值