以前有一個問題一直困擾着我,就是一個變量出了做用域,我覺得這個變量的內存就被回收了,其實不是這樣的,昨天問了一個高手,才豁然開朗,本身在看相關代碼的反彙編代碼,才知道原來真是這樣就。這個問題,我想簡單的說一下內存的分配VS回收&構造函數VS析構函數之間的關係。html
個人疑問:爲何p出了做用域,指向p的ptr還能讀到p中arr的內容,難道p出了做用域,尚未析構?ios
下面的內容會解答這個疑問,先說說跟這篇文章有關的內容。cookie
多是由於平時習慣的緣由,咱們在實例化一個對象的時候,每每是一條語句實現兩個功能:1分配內存;2調用構造函數函數
class A { public: A() { i=0; j=0; } ~A(){} int i; int j; }; A a1; A * a2=new A();
這兩中方式都是一步實現兩個操做,分配內存和調用構造函數,若是A沒寫構造函數,即沒有構造函數(編譯器也不會自動生成),固然就不須要調用構造函數。spa
其實這兩步是能夠分開的,A a1;這句分開不了這兩步,但A * a2=new A();是能夠分開,同等的代碼以下:scala
void* memory=operator new(sizeof(A));//分配內存3d
A* a2=new(memory) A();//在memory上調用A的構造函數指針
回收的時候,咱們能夠這樣寫:code
delete a2;//這句等同下面兩句htm
//a2->~A();
//operator delete(memory);
若是A沒有析構函數,固然delete時也不會調用,緣由請看個人博客:構造函數產生的點及緣由。
也就是說A* a=new A();delete a;這兩條語句,執行了四個操做:
分配內存->調用構造函數->調用析構函數->回收內存;
更多關於這四步分開的代碼:
而我今天要說的是,這四步是徹底能夠分開的。既然這四步是能夠分開的,那麼解答上面那個疑問就很簡單了。
Char* ptr;
{
Point p;
ptr=p;
}
P出了做用域,爲何ptr還能讀到他的內容,緣由很簡單:由於上面幾行代碼只執行了前面三步,最後一步回收內存,尚未執行。出了做用域,就會執行析構,沒說要回收內存,棧的內存要在方法返回以前纔回收,也就是說一個方法若是大量的分配內存是很容易爆棧,便是你讓棧中的變量出了做用域也沒用,請不要搞混了。棧內存在方法返回的時候纔回收,這一點就是爆棧的最重要緣由,爲何不是在變量出做用域的時候,調用完析構函數,就回收內存呢?我也不知道爲何?,看方法test11的反彙編代碼,的確是在方法返回的時候纔回收內存?
那個疑問的源碼以下:
#include "stdafx.h" #include <iostream> using namespace std; struct Point { char arr[10]; Point() { for(int i=0;i<9;i++) { arr[i]='a'; } arr[9]='\0'; } ~Point(){} operator char*() { return arr; } }; void test11() { char* ptr; { Point p; ptr=p; } cout<<ptr<<endl; } int _tmain(int argc, _TCHAR* argv[]) { { test11(); } system("pause"); return 0; }
test11的反彙編代碼以下:
void test11() { 010431F0 push ebp //ebp表示棧頂指針 010431F1 mov ebp,esp //esp表示棧當前指針 009C31F3 push 0FFFFFFFFh 009C31F5 push offset __ehhandler$?test11@@YAXXZ (9CA3C8h) 009C31FA mov eax,dword ptr fs:[00000000h] 009C3200 push eax 009C3201 sub esp,0E4h 009C3207 push ebx 009C3208 push esi 009C3209 push edi 009C320A lea edi,[ebp-0F0h] B::`scalar deleting destructor': 009C3210 mov ecx,39h 009C3215 mov eax,0CCCCCCCCh 009C321A rep stos dword ptr es:[edi] 009C321C mov eax,dword ptr [___security_cookie (9CF070h)] 009C3221 xor eax,ebp 009C3223 mov dword ptr [ebp-10h],eax 009C3226 push eax 009C3227 lea eax,[ebp-0Ch] 009C322A mov dword ptr fs:[00000000h],eax char* ptr; { Point p; 009C3230 lea ecx,[p] 009C3233 call Point::Point (9C1541h) 009C3238 mov dword ptr [ebp-4],0 ptr=p; 009C323F lea ecx,[p] 009C3242 call A::~A (9C1546h) 009C3247 mov dword ptr [ebp-18h],eax } 009C324A mov dword ptr [ebp-4],0FFFFFFFFh 009C3251 lea ecx,[p] 009C3254 call A::`scalar deleting destructor' (9C154Bh) cout<<ptr<<endl; 009C3259 mov esi,esp 009C325B mov eax,dword ptr [__imp_std::endl (9D039Ch)] 009C3260 push eax 009C3261 mov ecx,dword ptr [ebp-18h] 009C3264 push ecx 009C3265 mov edx,dword ptr [__imp_std::cout (9D03A0h)] 009C326B push edx 009C326C call std::operator<<<std::char_traits<char> > (9C132Fh) 009C3271 add esp,8 009C3274 mov ecx,eax 009C3276 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (9D0390h)] 009C327C cmp esi,esp 009C327E call @ILT+960(__RTC_CheckEsp) (9C13C5h) } 009C3283 push edx 009C3284 mov ecx,ebp 009C3286 push eax 009C3287 lea edx,[ (9C32C0h)] 009C328D call @ILT+350(@_RTC_CheckStackVars@8) (9C1163h) 009C3292 pop eax //pop開始出棧 注意;這裏纔開始回收內存 09C3293 pop edx 009C3294 mov ecx,dword ptr [ebp-0Ch] 009C3297 mov dword ptr fs:[0],ecx 009C329E pop ecx 009C329F pop edi 009C32A0 pop esi 009C32A1 pop ebx 009C32A2 mov ecx,dword ptr [ebp-10h] 009C32A5 xor ecx,ebp 009C32A7 call @ILT+65(@__security_check_cookie@4) (9C1046h) 009C32AC add esp,0F0h 009C32B2 cmp ebp,esp 009C32B4 call @ILT+960(__RTC_CheckEsp) (9C13C5h) 009C32B9 mov esp,ebp //棧頂指針和棧當前指針指向同一個地址,即棧的長度就是一個指針的長度 009C32BB pop ebp //棧頂指針彈出,如今棧空了 009C32BC ret
我有這個疑問的緣由就是:我覺得在出做用域的時候不只調用析構函數,還要回收內存,其實只是調用析構函數,內存在方法返回的時候纔回收。