內存的分配VS回收&構造函數VS析構函數

以前有一個問題一直困擾着我,就是一個變量出了做用域,我覺得這個變量的內存就被回收了,其實不是這樣的,昨天問了一個高手,才豁然開朗,本身在看相關代碼的反彙編代碼,才知道原來真是這樣就。這個問題,我想簡單的說一下內存的分配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  

我有這個疑問的緣由就是:我覺得在出做用域的時候不只調用析構函數,還要回收內存,其實只是調用析構函數,內存在方法返回的時候纔回收。

相關文章
相關標籤/搜索