深入c++中临时对象的析构时机的详解

c++中,临时对象一旦不需要,就会调用析构函数,释放其占有的资源;而具名对象则是与创建的顺序相反,依次调用析构函数。

c++源码:


代码如下:

class X  {
public:
   int i;
   int j;
   ~X() {}
   X() {}

};

int main() {
    X x1;
    X();
    x1.i = 1;
    X x2;

}

对应的汇编码:


代码如下:

_main    PROC

; 11   : int main() {

push    ebp
    mov    ebp, esp
    sub    esp, 24                    ; 为x1 临时对象 x2预留24byte空间

; 12   :     X x1;

lea    ecx, DWORD PTR _x1$[ebp];获取x1对象的首地址,作为隐含参数传入构造函数
    call    ??0X@@QAE@XZ                ; 为x1调用构造函数

; 13   :     X();

lea    ecx, DWORD PTR $T2559[ebp];获取临时对象首地址,作为隐含参数传入构造函数
    call    ??0X@@QAE@XZ                ; 为临时对象调用构造函数
    lea    ecx, DWORD PTR $T2559[ebp];获取临时对象首地址,作为隐含参数传入析构函数
    call    ??1X@@QAE@XZ                ; 为临时对象调用析构函数

; 14   :     x1.i = 1;

mov    DWORD PTR _x1$[ebp], 1;将1写给x1首地址处内存,即将1写入x1中的成员变量i中

; 15   :     X x2;

lea    ecx, DWORD PTR _x2$[ebp];获取x2的首地址,作为隐含参数传入构造函数
    call    ??0X@@QAE@XZ                ; 为x2调用构造函数

; 16   :    
; 17   :    
; 18   : }

lea    ecx, DWORD PTR _x2$[ebp];获取x2的首地址,作为隐含参数传入析构函数
    call    ??1X@@QAE@XZ                ; 为x2调用析构函数
    lea    ecx, DWORD PTR _x1$[ebp];获取x1的首地址,作为隐含参数传入析构函数
    call    ??1X@@QAE@XZ                ; 为x1调用析构函数
    xor    eax, eax
    mov    esp, ebp
    pop    ebp
    ret    0
_main    ENDP

从上面的汇编码可以看出,临时对象确实是在不需要之后就调用了析构函数,尽管它在x2对象之前被创建,但依然在x2对象之前被析构。而x1 x2析构函数调用顺序,是与他们构造函数的调用顺序相反。

再看下面的情况:

c++中的源码:


代码如下:

class X  {
public:
  int i;
  int j;
  int k;
  X() {}
  ~X() {}
};

int main() {
    X x1;
    X(), x1.i = 1;//这里有一条逗号运算符
    X x2;
}

这里,改造临时对象之后,有一个逗号表达式,而不是分号。

下面是汇编码:


代码如下:

; 12   : int main() {

push    ebp
    mov    ebp, esp
    sub    esp, 36                    ; 为x1 临时对象 x2预留36字节的空间

; 13   :     X x1;

lea    ecx, DWORD PTR _x1$[ebp];获取x1的的首地址,作为隐含参数传递给构造函数
    call    ??0X@@QAE@XZ                ; 为x1调用构造函数

; 14   :     X(), x1.i = 1;//这里有一条逗号运算符

lea    ecx, DWORD PTR $T2560[ebp];获取临时对象的首地址,作为隐含参数传递给构造函数
    call    ??0X@@QAE@XZ                ; 为临时对象调用构造函数
    mov    DWORD PTR _x1$[ebp], 1;将1赋给x1首地址处的内存,即给x1的成员变量i赋值1
    lea    ecx, DWORD PTR $T2560[ebp];获取临时变量的首地址,作为隐含参数传递给析构函数
    call    ??1X@@QAE@XZ                ; 为临时对象调用析构函数

; 15   :     X x2;

lea    ecx, DWORD PTR _x2$[ebp];获取x2的首地址,作为隐含参数传递给构造函数
    call    ??0X@@QAE@XZ                ; 为x2调用构造函数

; 16   : }

lea    ecx, DWORD PTR _x2$[ebp];获取x2的首地址,作为隐含参数传递给析构函数
    call    ??1X@@QAE@XZ                ; 为x2调用析构函数
    lea    ecx, DWORD PTR _x1$[ebp];获取x1的首地址,作为隐含参数传递给析构函数
    call    ??1X@@QAE@XZ                ; 为x1调用析构函数
    xor    eax, eax
    mov    esp, ebp
    pop    ebp
    ret    0
_main    ENDP

可以看到,与第一次不同的是,临时对象构造完毕之后,并没有立即调用析构函数,而是执行了逗号后面的赋值语句后,才调用的析构函数。

综上所述:

临时对象调用析构函数的时机是一条高级语言执行完毕的时候,而一条高级语言执行完毕的标志是分号。所以,临时对象调用析构函数的时机是碰到分号的时候

(0)

相关推荐

  • C++中的对象指针总结

    指向对象的指针在建立对象的时候,变异系统会给每一个对象分配一定的存储空间,以存放其成员.对象空间的起始地址就是对象的指针.可以定义一个指针变量,用来存放对象的指针. 一个简单的示例1.1: 复制代码 代码如下: #include<iostream>using namespace std;class Student{ public:  int num;  int score;  Student(int ,int );//声明构造函数  void Print();//声明输出信息函数};Stude

  • C++中对象的常引用总结

    直接传递对象名 用对象名做函数参数时,在函数调用时将建立一个新的对象,它是形参对象的拷贝. ================下面给出一个直接传递对象名的例子程序1.1================== 复制代码 代码如下: #include<iostream>using namespace std;class Time { public:  Time(int,int,int);//构造函数   void Print();//输出信息函数   void reset(Time t);//重置函数

  • C++中的对象数组详细解析

    类是对象的抽象,我们可以使用一个类来定义很多的对象,然后每个对象都有自己的属性. 当我们使用类来定义很多相同结构的对象的时候,我们可以采取对象数组的方法. 例如,一个班有50个学生,我们定义了一个学生类,该类的学生具有相同的数据成员和成员函数,我们就可以定义一个这样的数组. 复制代码 代码如下: Student stdu[50];//假设已经声明了Student类,定义stud数组,有50个元素 ======================对象数组的初始化====================

  • C++函数返回值为对象时,构造析构函数的执行细节

    看如下代码: 复制代码 代码如下: #include<iostream>class TestConstructor{public:    TestConstructor()    {        std::cout<<"TestConstructor()"<<std::endl;    }    ~TestConstructor()    {        std::cout<<"~TestConstructor()"

  • C++实现动态分配const对象实例

    本文实例讲述了C++实现动态分配const对象的方法.分享给大家供大家参考.具体方法分析如下: 一.创建 在C++中,允许动态创建const对象,格式如下: const int *p = new const int(128); 与其他常量一样,动态创建的const对象必须在创建时初始化,并且初始化后,其值不能改变. 二.删除 尽管不能改变const对象的值,但可以删除动态创建的const对象,格式如下: delete p; 这个和普通的对象一样,可以对其进行删除操作. 三.应用场景举例 1.加载

  • C++用new创建对象和不用new创建对象的区别解析

    我们都知道C++中有三种创建对象的方法,如下: 复制代码 代码如下: #include <iostream>using namespace std; class A{private:    int n;public:    A(int m):n(m)    {    }    ~A(){}}; int main(){    A a(1);  //栈中分配    A b = A(1);  //栈中分配    A* c = new A(1);  //堆中分配 delete c;    return

  • C++中的函数指针与函数对象的总结

    篇一.函数指针函数指针:是指向函数的指针变量,在C编译时,每一个函数都有一个入口地址,那么这个指向这个函数的函数指针便指向这个地址. 函数指针的用途是很大的,主要有两个作用:用作调用函数和做函数的参数. 函数指针的声明方法:数据类型标志符 (指针变量名) (形参列表):一般函数的声明为: int func ( int x );而一个函数指针的声明方法为:int (*func) (int x);前面的那个(*func)中括号是必要的,这会告诉编译器我们声明的是函数指针而不是声明一个具有返回型为指针

  • C++对象的动态建立与释放详解

    =============下面先给出一个new和delete基本应用的例子,回顾一下它的基本用法============ 复制代码 代码如下: #include<iostream>using namespace std;int main(){ int *p;//定义一个指向int型变量的指针p  p=new int(3);//开辟一个存放整数的存储空间,返回一个指向该存储空间的的地址  cout<<*p<<endl; delete p;//释放该空间  char *p_

  • C++内核对象封装单实例启动程序的类

    复制代码 代码如下: //File Name: Singleton.h#pragma once class Singleton{private:    CString strGUID;    CString strMapFileGUID; HANDLE m_hSingleton; public:    Singleton();    ~Singleton();public:    void AppStart (const HWND & hWnd) const;}; 复制代码 代码如下: //Fi

  • 解析c++中参数对象与局部对象的析构顺序的详解

    下面是c++的源码: 复制代码 代码如下: class X  {public:   int i;   int j;   ~X() {} };void f(X x) {  X x1;  x.i = 1;  x.j = 2; }int main() {    f(X());} 下面是main函数的汇编码: 复制代码 代码如下: _main    PROC ; 15   : int main() { push    ebp    mov    ebp, esp    sub    esp, 8;为临时

  • C++中Semaphore内核对象用法实例

    本文实例讲述了C++中Semaphore内核对象的用法,分享给大家供大家参考.具体方法如下: 复制代码 代码如下: // Semaphore.cpp : 定义控制台应用程序的入口点.  //    #include "stdafx.h"  #include <Windows.h>  #include <process.h>     HANDLE g_hSemaphore;  DWORD g_nConut1 = 0;  DWORD g_nConut2 = 0; 

随机推荐