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

2016-02-19 09:21 6 1 收藏

今天图老师小编要跟大家分享解析c++中参数对象与局部对象的析构顺序的详解,精心挑选的过程简单易学,喜欢的朋友一起来学习吧!

【 tulaoshi.com - 编程语言 】

下面是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

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

; 15   : int main() {

    push    ebp
    mov    ebp, esp
    sub    esp, 8;为临时对象预留8byte空间,由于没有显示定义构造函数,
              ;而且这种情况下编译器提供无用的默认构造函数,因此看不到构造函数的调用

; 16   :     f(X());

    mov    eax, DWORD PTR $T2560[ebp+4];将偏移临时变量的首地址4byte处内存中内容给eax,即将临时变量的成员变量j值给eax
    push    eax;将eax压栈
    mov    ecx, DWORD PTR $T2560[ebp];将临时变量首地址中的内容给ecx,即将临时变量中的成员变量i值给ecx
    push    ecx;将ecx压栈
               ;上面四句创建了临时变量的一份拷贝,作为参数调用f
    call    ?f@@YAXVX@@@Z                ; 调用函数f
    add    esp, 8;将栈顶指针下移8byte,释放为参数对象的提供的栈空间
    lea    ecx, DWORD PTR $T2560[ebp];将临时对象的首地址给ecx
    call    ??1X@@QAE@XZ                ; 为临时对象调用析构函数

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

; 17   : }

    xor    eax, eax
    mov    esp, ebp
    pop    ebp
    ret    0
_main    ENDP


从上面可以看出,产生的临时对象在函数调用完成退出后才调用析构函数。

下面是f函数的汇编码:
代码如下:

?f@@YAXVX@@@Z PROC                    ; f

; 9    : void f(X x) {

    push    ebp
    mov    ebp, esp
    sub    esp, 8;为局部对象x1预留8byte的空间

; 10   :   X x1;
; 11   :   x.i = 1;

    mov    DWORD PTR _x$[ebp], 1;把1写给参数对象首地址处,即把1写入参数对象的成员变量i

; 12   :   x.j = 2;

    mov    DWORD PTR _x$[ebp+4], 2;把2写入偏移参数对象首地址4byte处的内存,即把2写入参数对象的成员变量j

; 13   :    
; 14   : }

    lea    ecx, DWORD PTR _x1$[ebp];将局部变量x1的首地址给ecx
    call    ??1X@@QAE@XZ                ; 为x1调用析构函数
    lea    ecx, DWORD PTR _x$[ebp];将参数对象的首地址给ecx
    call    ??1X@@QAE@XZ                ; 为参数对象调用析构函数
    mov    esp, ebp
    pop    ebp
    ret    0
?f@@YAXVX@@@Z ENDP                    ; f
; Function compile flags: /Odtp
_TEXT    ENDS
;    COMDAT ??1X@@QAE@XZ
_TEXT    SEGMENT
_this$ = -4                        ; size = 4
??1X@@QAE@XZ PROC                    ; X::~X, COMDAT
; _this$ = ecx

; 6    :    ~X() {}

    push    ebp
    mov    ebp, esp
    push    ecx
    mov    DWORD PTR _this$[ebp], ecx
    mov    esp, ebp
    pop    ebp
    ret    0
??1X@@QAE@XZ ENDP

从上面的代码可以看出,参数对象和局部对象都是在函数退出之前调用析构函数。并且参数对象在局部对象调用析构函数之后再调用自己的析构函数。

来源:https://www.tulaoshi.com/n/20160219/1590473.html

延伸阅读
实在说,我对delphi的理解不是很深入,使用它写过一些小程序,不过鉴于borland的传奇,还是看了李维那本vcl内幕剖析,最终的感觉不是对delphi多么狂热,反而是另样的深入理解了c++的优缺点以及模式设计一些理念的深化,不过,仍旧对borland抱有尊敬,另外也羡慕那些使用delphi的人,控间好多啊,:)这个是我早期从bbs上收集的文章,暂...
用过C++进行过面向对象程序设计的用户都知道,程序中的对象很少单独存在。不考虑对象间的相互作用几乎是不可能的。所以,标识对象间的关系或建立对象间的消息连接是面向对象程序设计的一项重要任务。         本文着重从C++程序设计的角度,提出一种建立对象间消息连接的实用方法。假如你想具体了解面...
结构体和类有相同的特性,但又有很大的区别,类是构成面向对象编程的基础,但它是和结构体有着机器密切的关系。 我们在c语言中创建一个结构体我们使用如下方法: C++ 代码 //程序作者:管宁   //所有稿件均有版权,如要转载,请务必闻名出处和作者 strUCt test { priva...
假如一个人自称为程序高手,却对内存一无所知,那么我可以告诉你,他一定在吹牛。 !-- frame contents -- !-- /frame contents -- 用C或C++写程序,需要更多地关注内存,这不仅仅是因为内存的分配是否合理直接影响着程序的效率和性能,更为主要的是,当我们操作内存的时候一不小心就会出现问题,而且很多时候,这些问题都是不易发觉...
上一篇我们对合成确省的构造函数做了一个了解,这一篇我们继续看看构造函数这个有趣的东西. Copy Constructor是什么?我们经常看到代码中有一些这样的函数调用方式X(X&) (X of X ref). 这个函数用用户自定义类型作为参数,那它的参数的构造便是由Copy Constructor负责的. 可见这个玩意非常重要,实际上Copy Constructor是由编译器自...

经验教程

716

收藏

74
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部