栈上的对象你以为是真的“自动释放”的吗

2020-06-28  本文已影响0人  xiaoliang1

每次都说栈上的对象是自动释放的。其实你很不明白为什么这么神奇,当你问时,别人还牛逼哄哄的说,哪有那么为什么,记住自动释放释放就行了。这无疑上学时“背课文”。问也不能问。还会遭到训斥,这是让人不能接受的时,你不能提出你的疑问。
今天就告诉你,其实很简单,就是出栈入栈导致的,分布在栈上的对象,在函数执行完毕。栈会恢复已经开辟的栈空间,所以你在反汇编时经常看到函数开头

sub         esp,0E8h

而在结尾时:

add         esp,0F4h  

(当然还有一些出栈,恢复上一个函数的参数等等一些环境)
这样这个函数执行完毕时,这个栈上的对象可能还在内存中,但当进行其他函数调用,或者其他操作时,这个对象的数据就会被覆盖掉。
所以,你就理算当然以为的“自动释放”,不用管理.
那你可能会说哪有这么简单,他会调用一些析构函数或者销毁函数。是自动释放时调用的。
其实不是这样的,因为调用这是编译器的做的,你看着表面没有调用析构函数或者销毁函数。这都是编译器帮你写了汇编。来通知你,释放前的通知,你可以做一些事,释放这个对象销毁前,要销毁的堆上数据。
C++这个语言都是站编译器的角度看问题的。底层没什么数据结构和很牛逼的设计来支持,所以只能静态语言了。只能靠编译器隐式帮你做一些操作。
下面用代码+汇编代码来看来:

void test3() {

    vector<int>p1;
    p1.push_back(1);

}

C++代码就这么简单,p1会在这个函数执行完时,调用vector<int,std::allocator<int> >::~vector<int,std::allocator<int> >这个析构函数,返回代码:

void test3() {
00CA8E60  push        ebp  
00CA8E61  mov         ebp,esp  
00CA8E63  push        0FFFFFFFFh  
00CA8E65  push        0CB196Dh  
00CA8E6A  mov         eax,dword ptr fs:[00000000h]  
00CA8E70  push        eax  
00CA8E71  sub         esp,0E8h  
00CA8E77  push        ebx  
00CA8E78  push        esi  
00CA8E79  push        edi  
00CA8E7A  lea         edi,[ebp-0F4h]  
00CA8E80  mov         ecx,3Ah  
00CA8E85  mov         eax,0CCCCCCCCh  
00CA8E8A  rep stos    dword ptr es:[edi]  
00CA8E8C  mov         eax,dword ptr [__security_cookie (0CB8008h)]  
00CA8E91  xor         eax,ebp  
00CA8E93  mov         dword ptr [ebp-10h],eax  
00CA8E96  push        eax  
00CA8E97  lea         eax,[ebp-0Ch]  
00CA8E9A  mov         dword ptr fs:[00000000h],eax  
00CA8EA0  mov         ecx,offset _5BA660AA_ConsoleApplication16@cpp (0CBA028h)  
00CA8EA5  call        @__CheckForDebuggerJustMyCode@4 (0CA14B5h)  

    vector<int>p1;
00CA8EAA  push        10h  
00CA8EAC  lea         ecx,[p1]  
00CA8EAF  call        std::vector<int,std::allocator<int> >::__autoclassinit2 (0CA12FDh)  
00CA8EB4  lea         ecx,[p1]  
00CA8EB7  call        std::vector<int,std::allocator<int> >::vector<int,std::allocator<int> > (0CA131Bh)  
00CA8EBC  mov         dword ptr [ebp-4],0  
    p1.push_back(1);
00CA8EC3  mov         dword ptr [ebp-0F0h],1  
    p1.push_back(1);
00CA8ECD  lea         eax,[ebp-0F0h]  
00CA8ED3  push        eax  
00CA8ED4  lea         ecx,[p1]  
00CA8ED7  call        std::vector<int,std::allocator<int> >::push_back (0CA17EEh)  

}
00CA8EDC  mov         dword ptr [ebp-4],0FFFFFFFFh  
00CA8EE3  lea         ecx,[p1]  
00CA8EE6  call        std::vector<int,std::allocator<int> >::~vector<int,std::allocator<int> > (0CA14C9h)  
00CA8EEB  push        edx  
00CA8EEC  mov         ecx,ebp  
00CA8EEE  push        eax  
00CA8EEF  lea         edx,ds:[0CA8F28h]  
00CA8EF5  call        @_RTC_CheckStackVars@8 (0CA1514h)  
00CA8EFA  pop         eax  
00CA8EFB  pop         edx  
00CA8EFC  mov         ecx,dword ptr [ebp-0Ch]  
00CA8EFF  mov         dword ptr fs:[0],ecx  
00CA8F06  pop         ecx  
00CA8F07  pop         edi  
00CA8F08  pop         esi  
00CA8F09  pop         ebx  
00CA8F0A  mov         ecx,dword ptr [ebp-10h]  
00CA8F0D  xor         ecx,ebp  
00CA8F0F  call        @__security_check_cookie@4 (0CA141Fh)  
00CA8F14  add         esp,0F4h  
00CA8F1A  cmp         ebp,esp  
00CA8F1C  call        __RTC_CheckEsp (0CA14CEh)  
00CA8F21  mov         esp,ebp  
00CA8F23  pop         ebp  
00CA8F24  ret  

}

很明显编译主动调用了std::vector<int,std::allocator<int> >::~vector<int,std::allocator<int> > (0CA14C9h) 这个析构函数
,如果成员变量指向了堆上的数据,请释放,并置NULL;
这只是冰山一角,好多都是编译器帮你做了什么。而且还是隐式的。
栈上的数据就如果在覆盖前,其实还是能拿到的、

Base test4() {

    Base p1;
    p1.a = 10;
    printf("0x%x", &p1);
    return p1;
}



int main()
{
    Base p1=  test4();
    return EXIT_SUCCESS;
}

你可以打断点查看main这个p1,即使调用析构函数.在覆盖前,你还是可以看到的,这个对象还在不子,就看覆盖没有覆盖。
这是帮助你理解自动释放。我自己的心得,之前也从未想过这个问题。可能你早已知道这些,但是我的心得。

上一篇 下一篇

猜你喜欢

热点阅读