Effective_C++:10、如果你寫了一個operator new,請對應寫一個operator delete

10、如果你寫了一個operator new,請對應寫一個operator delete

1、何時需要寫一個operator new

        缺省版的operator new和delete功能已經足夠了,但不可避免的是他可能需要花費更多的內存,來提高效率。對於一些需要動態分配大量小額空間的程序而言,更需要提高效率。

class AirplaneRep {...};//表示一個Airplane對象

class Airplane {
public:
    ...
private:
    AirplaneRep *rep;//指針,指向一個Airplane對象
};

        如上,Airplane只內含一個指針,當調用operator new時獲得的內存要比存儲該指針的所需內存多,因爲要讓operator new與operator delete溝通。爲了讓operator delete知曉operator new分配了多少內存,通常要在他所傳回的內存前加掛一些數據,來說明配置的區塊大小

        對於小型對象如Airplane而言,這個額外的內存可能比真正需要的內存都多。若軟件運行在內存十分寶貴的環境下,用缺省的operator new分配內存就比較奢侈了。此時,可以根據每個Airplane對象的大小相同,撰寫一個operator new,而不需要加掛額外數據。

        爲此,撰寫operator new,一種方法是:先用默認的operator new配置一大塊內存,可容納大量Airplane對象。而每一個Airplane對象需要的內存都從這塊內存挖取。剩餘尚未被利用的內存用鏈表來管理:利用union,當內存未被配置時,他是鏈表的next指針;當內存被配置爲Airplane對象時,他是*rep。

class Airplane {
public:
    static void * operator new (size_t size);
    ...
private:
    union {
        AirplaneRep *rep;
        Airplane *next;
    };
    static const int BLOCK_SIZE;
    static Airplane *headofFreeList;
};

        這裏,添加了operator new聲明、union、常數(指定每個被配置區塊的大小)、static指針(記錄freelist的頭部,整個類共享一個)。

void * Airplane::operator new (size_t size)
{
    if(size != sizeof(Airplane))
        return ::operator new(size);
    Airplane *p = headofFreeList;
    if(p)
        headofFreeList = p->next;
    else {
        Airplane *newBlock = static_castz<Airplane *>(::operator new(BLOCK_SIZE * sizeof(Airplane)));
        for(int i = 1; i < BLOCK_SIZE; ++i)
            newBlock[i].next = &newBlock[i + 1];
        newBlock[BLOCK_SIZE - 1].next = 0;
        p = newBlock;
        headofFreeList = &newBlock[1];
    }
    return p;
}
Airplane *Airplane::headofFreeList;
const int Airplane::BLOCK_SIZE = 512;

2、爲啥要對應寫一個operator delete

        現在我們已經寫了一個operator new,沒有聲明operator delete。當使用delete時,他會調用::operator delete來處理Airplane::operator new。而::operator delete認爲傳回的這個指針是帶有額外數據的,實際上並沒有,必然會導致錯誤。因此,一併寫出operator new和operator delete。

3、對應寫一個operator delete

class Airplane {
public:
    ...
    static void operator delete (void *deadObject, size_t size);
};
void Airplane::operator delete (void *deadObject, size_t size)
{
    if(deadObject == 0) return;
    if(size != sizeof(Airplane)) {
        ::operator delete(deadObject);
        return;
    }
    Airplane *carcass = static_cast<Airplane *>(deadObject);
    carcass->next = headofFreeList;
    headofFreeList = carcass;
}
        這裏,使用delete並沒有釋放這些內存,而是把它放回內存池,即freelist內,這些內存仍在我們的掌控之內,故沒有發生內存泄漏。同時,內存池的策略會使得即便頻繁使用new、delete也不會那麼慢。
        由此,可以想象,可以將配置固定大小區塊的內存配置器概念包裝起來,即用來配置管理固定大小的內存塊而不依賴於特定的類,而在具體的類中,只需使用給內存配置器配置管理內存即可。
class Pool {
public:
    Pool (size_t n);
    void * alloc (size_t n);
    void free (void *p, size_t n);
    ~Pool ();
};
class Airplane {
public:
    ...
    static void * operator new (size_t size);
    static void operator delete (void *p, size_t size);
private:
    AirplaneRep *rep;
    static Pool memPool;
};

inline void * Airplane::operator new (size_t size)
{ return memPool.alloc(size); }

inline void Airplane::operator delete (void *p, size_t size)
{ memPool.free(p, size); }
Pool Airplane::memPool(sizeof(Airplane));
        如此,Airplane與內存管理的細節沒什麼關係,都被隱藏在Pool中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章