每次都说栈上的对象是自动释放
的。其实你很不明白为什么这么神奇,当你问时,别人还牛逼哄哄的说,哪有那么为什么,记住自动释放释放就行了。这无疑上学时“背课文”。问也不能问。还会遭到训斥,这是让人不能接受的时,你不能提出你的疑问。
今天就告诉你,其实很简单,就是出栈入栈导致的,分布在栈上的对象,在函数执行完毕。栈会恢复已经开辟的栈空间,所以你在反汇编时经常看到函数开头
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,即使调用析构函数
.在覆盖前,你还是可以看到的,这个对象还在不子,就看覆盖没有覆盖。
这是帮助你理解自动释放
。我自己的心得,之前也从未想过这个问题。可能你早已知道这些,但是我的心得。
网友评论