C++內(nèi)存管理基礎(chǔ)之new&delete

字號:

內(nèi)存管理的基礎(chǔ)是要知道怎么獲得以及釋放內(nèi)存,如你所知,在C/C++中就是調(diào)用new和delete操作。
    1. 分清operator new和new operator
        全局函數(shù)operator new通常這樣聲明:
    void * operator new(size_t size);
    返回值類型是void*,表示其返回的是一個未經(jīng)處理(raw)的指針,指向未初始化的內(nèi)存。參數(shù)size_t確定分配多少內(nèi)存。你能增加額外的參數(shù)重載函數(shù)operator new,但是第一個參數(shù)類型必須是size_t。頭文件中有一個很好的重載的例子,那就是placement new,它看上去象這樣:
    void * operator new(size_t, void *location)
    {
        return location;
    }
    這初看上去有些陌生,但它卻是new操作符的一種常見重載方法,使用一個額外的變量buffer,當(dāng)new操作符隱含調(diào)用operator new函數(shù)時,把這個變量傳遞給它。被調(diào)用的operator new函數(shù)除了持有強制的參數(shù)size_t外,還必須接受void*指針參數(shù),指向構(gòu)造對象占用的內(nèi)存空間。未被使用的(但是強制的)參數(shù)size_t沒有參數(shù)名字,以防止編譯器警告說它未被使用。在使用placement new的情況下,調(diào)用者已經(jīng)獲得了指向內(nèi)存的指針,因為調(diào)用者知道對象應(yīng)該放在哪里。placement new需要做的就是返回傳遞給它的指針。
    我們更經(jīng)常使用的new是new操作符(new operator),而非操作符new(operator new),如當(dāng)你使用new操作符構(gòu)建一個對象的時候,實際上做了兩件事情,一是調(diào)用operator new函數(shù)獲取內(nèi)存,二是調(diào)用對象的構(gòu)造函數(shù),如:
    string *ps = new string("Hello, world!");
    它完成與下面代碼相似的功能:
    void *memory = operator new(sizeof(string)); // 為String對象得到未經(jīng)處理的內(nèi)存
    call string::string("Hello, world!") on *memory; // 調(diào)用構(gòu)造函數(shù)初始化內(nèi)存中的對象
    string *ps = static_cast(memory); // ps指針指向新的對象
     注意第二步中構(gòu)造函數(shù)的調(diào)用只能由編譯器完成,用戶是不允許這樣操作的,也就是說如果你想建立一個堆對象就必須用new操作符,不能直接像上面一樣調(diào)用構(gòu)造函數(shù)來初始化堆對象。
     new操作符(new operator)是編譯器內(nèi)置的,其行為被語言固定下來,不受用戶控制。但是它們所調(diào)用的內(nèi)存分配函數(shù)也就是操作符new(operator new)則可以根據(jù)需要進行重載。試著回顧new操作符(new operator)與操作符new(operator new)的關(guān)系,如果你想在堆上建立一個對象,應(yīng)該用new操作符。它既分配內(nèi)存又為對象調(diào)用構(gòu)造函數(shù)。如果你僅僅想分配內(nèi)存,就應(yīng)該調(diào)用operator new函數(shù),它不會調(diào)用構(gòu)造函數(shù)。如果你想定制自己獨有的內(nèi)存分配過程,你應(yīng)該重載全局的operator new函數(shù),然后使用new操作符,new操作符會調(diào)用你定制的operator new。如果你想在一塊已經(jīng)獲得指針的內(nèi)存里建立一個對象,應(yīng)該使用placement new。
     最后需要記住的一點是,delete和new一樣具有以上的特性,只是需要注意的一點是delte操作符中是首先調(diào)用對象的析構(gòu)函數(shù),然后再調(diào)用operator delete函數(shù)的。
    2. 針對數(shù)組的new[]和delete[]操作
     建立數(shù)組時new操作符(new[])的行為與單個對象建立(new)有少許不同:
    第一是內(nèi)存不再調(diào)用用operator new函數(shù)進行分配,代替以operator new[]函數(shù)(常稱作array new)。它與operator new一樣能被重載,允許定制數(shù)組的內(nèi)存分配,就象定制單個對象內(nèi)存分配一樣。
    第二個不同是new[]操作時調(diào)用構(gòu)造函數(shù)的數(shù)量。對于new[]而言,在數(shù)組里的每一個對象的構(gòu)造函數(shù)都必須被調(diào)用。
     delete[]操作符的語義基本上和new[]相同,他們的實現(xiàn)類似這樣:
    void * operator new[](size_t size)
    {
          cout << "new size of array in new[](): " << size << endl;
          int *g =(int *) malloc(sizeof(size));
          return g;
    }
    void operator delete[](void* p)
    {
     cout << "delete address of array pointer in delete[](): " << p << endl;
     free(p);
    }
    3. operator new和delete函數(shù)的實現(xiàn)
     operator new實際上總是以標(biāo)準(zhǔn)的C malloc()完成,雖然并沒有規(guī)定非得這么做不可。同樣,operator delete也總是以標(biāo)準(zhǔn)得C free()來實現(xiàn),不考慮異常處理的話他們類似下面的樣子:
    extern void* operator new( size_t size )
    {
     if( size == 0 )
     size = 1; // 這里保證像 new T[0] 這樣得語句也是可行的
           void *last_alloc;
           while( !(last_alloc = malloc( size )) )
           {
           if( _new_handler )
           ( *_new_handler )();
           else
           return 0;
           }
           return last_alloc;
    }
    extern void operator delete( void *ptr )
    {
           if(ptr) // 從這里可以看出,刪除一個空指針是安全的
           free( (char*)ptr );
    }