不同作用域对象的构造析构调用时机

全局变量和全局静态变量

使用vc栈回溯发现
在这里插入图片描述
这里构造的栈结构
在这里插入图片描述

因为atexit的参数的c约定回调,而析构是thiscall,调用约定,所以内部必须包含一层E2才可以,在使用E2调用析构,而不是直接注册析构
atexit可以注册多个回调,而这些会是一个线性表,里面储存了你注册的函数地址.当main函数结束的时候会调用

1.会在ininterm里面进行初始化动作
2.会产生代理函数,这个代理函数是为了使ininterm函数的代码正常初始化而产生的一个统一接口的函数,暂且称为E4 (名字可能不一样)
3.E4函数代理是为了统一接口,其内部又调用了 构造函数代理 (E1),和析构函数代理(E3)
4.E1代理函数是为了统一参数用的,其内部是调用构造的,如果是有参数构造,则在E1代理函数内部可以看到传参的。
5.E3代理函数是为了注册析构函数的,为了使atexit函数正常运行而注册的(atexit和ininterm类似,一个从前往后,一个从后往前)
6.E2是E3内部给atexit函数注册的回调,这样在析构的时候则调用E2即可.
7.E2函数内部是真正的调用析构的

静态局部变量

在这里插入图片描述
第一次调用会从teb里的tls区域取出标志位
调用构造并同时注册函数
如果是第二次则会检查标志位,如果有标志位则跳过这个区域

对象传参

特征

1.函数外会调用拷贝构造

  • 如果没有默认的拷贝构造
  • 编译器会做串操作指令拷贝到栈顶
    在这里插入图片描述

2.this指针在栈顶(有默认的拷贝构造)

  • 汇编语句:mov ecx, esp
    在这里插入图片描述

3.函数内会调用析构

  • 基于参数的析构
    在这里插入图片描述
    代码示例
class MyTest
{
public:
    MyTest(int nNumber)
    {
        m_nAge = nNumber;
    }
    MyTest(MyTest& obj)
    {
        m_nAge = obj.m_nAge;
    }
    ~MyTest()
    {
        printf("~MyTest()");
    }

public:
    int m_nAge;
    int m_nclass[50];
};


MyTest Test2(8);

void ShowClass(MyTest test)
{
    printf("%d", test.m_nAge);
    return;
}

int main(int argc)
{
    MyTest Test(argc);        

    ShowClass(Test);           //结构体传参调用拷贝构造

    return 0;
}

函数外
在这里插入图片描述
函数内
在这里插入图片描述

对象作返回值

特征

1.继承结构体特征(它会默认给我们生成一个参数传入,如果有别的参数,这个参数默认在第一个)
2.函数内构造,函数外析构(有临时对象会构造两个,析构两次)
区分是否产生临时对象
1.不产生临时对象

  • 如果为引用时,生命周期和作用域和引用一样MyTest &t = Getobj(int)
lass MyTest
{
public:
    MyTest(int nNumber)
    {
        m_nAge = nNumber;
    }
    MyTest(const MyTest& obj)
    {
        m_nAge = obj.m_nAge;
    }
    ~MyTest()
    {
        printf("~MyTest()");
    }

public:
    int m_nAge;
    int m_nclass[50];
};


MyTest GetObj(int nNumber)
{
    MyTest test(nNumber);
    return test;
}


int main(int argc)
{
    MyTest Test = GetObj(argc);      //直接用返回值定义变量 此处不产生临时变量

    printf("%d", Test.m_nAge);

    return 0;
}

在这里插入图片描述
在这里插入图片描述
2.产生临时对象

  • 临时对象会在函数内构造
  • 出函数遇到第一个分号之前析构
class MyTest
{
public:
    MyTest(int nNumber)
    {
        m_nAge = nNumber;
    }
    MyTest(const MyTest& obj)
    {
        m_nAge = obj.m_nAge;
    }
    ~MyTest()
    {
        printf("~MyTest()");
    }

public:
    int m_nAge;
    int m_nclass[50];
};


MyTest GetObj(int nNumber)
{
    MyTest test(nNumber);
    return test;
}


int main(int argc)
{
    MyTest Test(argc); 

    Test = GetObj(argc + 5);                 //此处会产生临时变量

    printf("%d", Test.m_nAge);

    return 0;
}

在这里插入图片描述
在这里插入图片描述

堆对象

特征
  1. 开始会调用new申请空间,并伴随一段大跳
    1.1 如果申请到空间则执行构造(返回值为1)
    1.2 如果没申请到空间则不执行构造(返回值为0)
    在这里插入图片描述

2.调用delete的时候,会传参1并调用代理析构函数(参数1可以表明是一个堆对象)
在这里插入图片描述
3.如果代理析构函数参数为0,则表明是手动调用析构,此时不会释放空间Test.~MyTest()

class MyTest
{
public:
    MyTest()
    {
        m_nAge = 5;
        printf("MyTest()");
    }
    MyTest(const MyTest& obj)
    {
        m_nAge = obj.m_nAge;
    }
    ~MyTest()
    {
        printf("~MyTest()");
    }

public:
    int m_nAge;
    int m_nclass[50];
};


int main(int argc)
{
    MyTest* Test = new MyTest;

    printf("%d", Test->m_nAge);

    delete Test;

    return 0;
}

函数外
在这里插入图片描述
函数内
在这里插入图片描述

对象数组

代码示例

class MyTest
{
public:
    MyTest()
    {
        m_nAge = 5;
        printf("MyTest()");
    }
    MyTest(const MyTest& obj)
    {
        m_nAge = obj.m_nAge;
    }
    ~MyTest()
    {
        printf("~MyTest()");
    }

public:
    int m_nAge;
    int m_nclass[50];
};


int main(int argc)
{
    MyTest Test[5];

    printf("%d", Test[0].m_nAge);

    return 0;
}

1.定义的时候调用构造迭代器代理函数

  • 参数1:首地址
  • 参数2:每个类的大小
  • 参数3:类个数
  • 参数4:构造函数指针
  • 参数5:析构函数指针
    2.析构的时候调用析构迭代器代理函数(相比构造少一个参数)
    在这里插入图片描述
    3.代理函数内循环调用构造/析构
  • 如果是构造,循环执行顺序按数组下标升序
  • 如果是析构,循环执行顺序按数组下标降序
    在这里插入图片描述

堆对象数组

代码示例

class MyTest
{
public:
    MyTest()
    {
        m_nAge = 5;
        printf("MyTest()");
    }
    MyTest(const MyTest& obj)
    {
        m_nAge = obj.m_nAge;
    }
    ~MyTest()
    {
        printf("~MyTest()");
    }

public:
    int m_nAge;
    int m_nclass[50];
};


int main(int argc)
{
    MyTest* Test = new MyTest[5];

    Test.~MyTest();

    printf("%d", Test[0].m_nAge);

    delete[] Test;

    return 0;
}

1.开始会调用new申请空间,并伴随一段大跳

  • 如果申请到空间则执行构造(返回值为1)
  • 如果没申请到空间则不执行构造(返回值为0)
    在这里插入图片描述

2.申请到空间,执行代理函数

  • 参数1:首地址
  • 参数2:每个类的大小
  • 参数3:类个数
  • 参数4:构造函数指针
  • 参数5:析构函数指针
    在这里插入图片描述

3.调用delete的时候,会传参3并调用代理析构函数(参数3可以表明是一个堆对象数组)

  • 代理函数外
    在这里插入图片描述
  • 代理函数内
    在这里插入图片描述
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值