類的operator new與operator delete的重載

爲什麼有必要寫自己的operator new和operator delete?

答案通常是:爲了效率。
缺省版本的operator new是一種通用型的內存分配器,它必須可以分配任意大小的內存塊。同樣,operator delete也要可以釋放任意大小的內存塊。operator delete想弄清它要釋放的內存有多大,就必須知道當初operator new分配的內存有多大。有一種常用的方法可以讓operator new來告訴operator delete當初分配的內存大小是多少,就是在它所返回的內存裏預先附帶一些額外信息,用來指明被分配的內存塊的大小。
缺省的operator new和operator delete具有非常好的通用性,它的這種靈活性也使得在某些特定的場合下,可以進一步改善它的性能。尤其在那些需要動態分配大量的但很小的對象的應用程序裏,情況更是如此。

內存池:內存池的作用主要也是爲了效率。通過一次申請比較大的內存空間,來避免小空間內存的頻繁申請和釋放,每次需要爲對象分配內存空間時,在已經申請的大空間內分配。空閒區被按照對象大小劃分爲若干塊,塊與塊之間通過鏈表組織起來。

參考代碼:

class airplanerep  // 表示一個飛機對象
{
public:
    airplanerep(int id,const string & s,const string & d)
    {
        ID = id;
        start = s;
        dest = d;
    }

    ~airplanerep()
    {
        cout<<"airplanerep destructor!"<<endl;
    }

    int getID() const
    {
        return ID;
    }

private:
    int ID;
    string start;
    string dest;
};

// 注意airplane和airplanerep含有一樣的成員函數,它們的接口完全相同
// 類airplane實際上是個句炳類(Handle class)
class airplane // 修改後的類 — 支持自定義的內存管理
{
public:
    airplane(int id,const string & s,const string & d)
    {
        rep = new airplanerep(id,s,d);
    }

    ~airplane()
    {
        delete rep;
    }

    int getID() const
    {
        return rep->getID();
    }

    static void * operator new(size_t size);
    static void operator delete(void *deadobject,size_t size);

private:
    union
    {
        airplanerep *rep;  // 用於被使用的對象
        airplane *next;    // 用於沒被使用的(在自由鏈表中)對象
    };

    // 類的常量,指定一個大的內存塊中放多少個
    // airplane對象,在後面初始化
    static const int block_size;
    static airplane *headoffreelist;
};

airplane *airplane::headoffreelist; //內存池鏈表頭
const int airplane::block_size = 512; //內存池塊的個數

void * airplane::operator new(size_t size)
{
    // 把“錯誤”大小的請求轉給::operator new()處理;
    if(size != sizeof(airplane))
        return ::operator new(size);

    airplane *p = headoffreelist; // p指向自由鏈表的表頭

    // p 若合法,則將表頭移動到它的下一個元素
    if(p)
        headoffreelist = p->next;
    else
    {
        // 自由鏈表爲空,則分配一個大的內存塊,
        // 可以容納block_size個airplane對象
        airplane *newblock = static_cast<airplane*>(::operator new(block_size * sizeof(airplane)));

        // 將每個小內存塊鏈接起來形成一個新的自由鏈表
        // 跳過第0個元素,因爲它要被返回給operator new的調用者
        for (int i = 1; i < block_size-1; ++i)
            newblock[i].next = &newblock[i+1];

        // 用空指針結束鏈表
        newblock[block_size-1].next = 0;

        // p 設爲表的頭部,headoffreelist指向的
        // 內存塊緊跟其後
        p = newblock;
        headoffreelist = &newblock[1];
    }

    return p;
}

// 傳給operator delete的是一個內存塊, 如果
// 其大小正確,就加到自由內存塊鏈表的最前面
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;
}

int main()
{
    airplane *pa = new airplane(101,"shanghai","beijing"); // 第一次分配: 得到大塊內存,生成自由鏈表,等
    cout<<pa->getID()<<endl;
    delete pa;

    airplane *pb = new airplane(102,"shanghai","beijing");
    cout<<pb->getID()<<endl;
    delete pb;

    return 0;
}

airplane類的說明:

airplane實際上是個句炳類(Handle class),通過指針airplanerep * rep指向一個具體的實現,airplane和airplanerep含有一樣的成員函數。句柄類實際上都做了些什麼:它只是把所有的函數調用都轉移到了對應的主體類中,主體類真正完成工作。
operator new函數負責內存池鏈表的創建,內存池鏈表的每個塊大小和類airplane一樣,每次生成對象的時候分配一個塊給對象。
operator delete函數負責收回每個對象的內存塊,重新添加到內存池鏈表。
operator new和operator delete需要同時工作,那麼你寫了operator new,就也一定要寫operator delete。
一個聯合(使得rep和next域佔用同樣的空間),一個常量(指定大內存塊的大小),一個靜態指針(跟蹤自由鏈表的表頭)。表頭指針聲明爲靜態成員很重要,因爲整個類只有一個自由鏈表,而不是每個airplane對象都有。
注意:::operator new返回的內存塊是從來沒有被airplane::operator delete釋放。內存泄漏和內存池有一個重要的不同之處:內存泄漏會無限地增長,而內存池的大小決不會超過客戶請求內存的最大值。

以上內容基本都來自《Effective C++》,稍作修改。

一個內存分配器基類

CachedObj的功能:類似於內存池的功能。分配和管理已經分配但未構造對象的自由列表。operator new返回自由列表中的一個元素,當自由列表爲空時,operator new分配新的原始內存。operator delete在撤銷對象時將元素放回自由列表。

template<class T>
class CachedObj
{
public:
    void * operator new(size_t sz);
    void operator delete(void* p, size_t sz);
    virtual ~CachedObj() {}
protected:
    T * next;
private:
    static void add_to_freelist(T * p);
    static allocator<T> alloc_mem;
    static T * free_store;
    static const size_t chunk;
};

template<class T>
allocator<T> CachedObj<T>::alloc_mem;

template<class T>
T* CachedObj<T>::free_store = NULL;

template<class T>
const size_t CachedObj<T>::chunk = 64;

template<class T>
void *CachedObj<T>::operator new(size_t sz)
{
    if(sz != sizeof(T))
        return ::operator new(sz);

    if (!free_store)
    {
        T * array = alloc_mem.allocate(chunk);
        for(size_t i = 0; i != chunk; ++i)
            add_to_freelist(&array[i]);
    }

    T *p = free_store;
    free_store = free_store->next;
    return p;
}

template<class T>
void CachedObj<T>::operator delete(void * p,size_t sz)
{
    if(p == NULL) return;
    if(sz != sizeof(T))
    {
        ::operator delete(p);
        return;
    }
    add_to_freelist(static_cast<T*>(p));
}

template<class T>
void CachedObj<T>::add_to_freelist(T *p)
{
    p->next = free_store;
    free_store = p;
}

如何使用這個類:

// 表示一個飛機對象
class airplanerep: public CachedObj<airplanerep>
{
public:
    airplanerep(int id,const string & s,const string & d)
    {
        ID = id;
        start = s;
        dest = d;
    }

    ~airplanerep()
    {
        cout<<"airplanerep destructor!"<<endl;
    }

    int getID() const
    {
        return ID;
    }

private:
    int ID;
    string start;
    string dest;
};


int main()
{
    airplanerep *pa = new airplanerep(101,"shanghai","beijing");
    cout<<pa->getID()<<endl;
    delete pa;

    airplanerep *pb = new airplanerep(102,"shanghai","beijing");
    cout<<pb->getID()<<endl;
    delete pb;

    return 0;
}


 


 

 

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