爲什麼有必要寫自己的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;
}