應(yīng)用程序的設(shè)計(jì)中,我們所說(shuō)的內(nèi)存管理就是將系統(tǒng)要處理的內(nèi)存分配和釋放接管過(guò)來(lái),內(nèi)存池是常用的一種設(shè)計(jì)思路。內(nèi)存池是在程序的一開(kāi)始就分配一大塊的內(nèi)存,在后續(xù)需要使用內(nèi)存的地方就直接從內(nèi)存池中分配出來(lái)一塊給程序使用,這樣就避免了反復(fù)的向系統(tǒng)申請(qǐng)和釋放內(nèi)存,從而造成性能上的損失。另一方面,統(tǒng)一的管理還有利于避免內(nèi)存泄露的出現(xiàn),因?yàn)榇罅康牡胤椒峙鋬?nèi)存,容易出現(xiàn)忘記了寫(xiě)delete的情況。要想了解內(nèi)存管理該怎么做,首先就需要知道C++中給我們提供了哪些東西,我們利用這些東西又可以干什么。
前面兩篇中主要講在系統(tǒng)的各個(gè)層面上的內(nèi)存管理的函數(shù)接口,以及它們的使用方式。那些都是系統(tǒng)/Runtime提供給我們的。這一篇中我們就來(lái)看看我們可以在這些操作中進(jìn)行自定義的一些修改。重載是面向?qū)ο笳Z(yǔ)言的一個(gè)重要的特性。使用重載我們可以在多層繼承關(guān)系中,讓子類能夠運(yùn)行帶有自己特色的函數(shù)。
1. 調(diào)用流程以及可重載部分
首先來(lái)看我們?cè)贑++中使用內(nèi)存管理的操作的時(shí)候執(zhí)行的流程,以及這些流程中那些步驟是允許我們?nèi)ブ剌d的。

上圖中,我們?cè)赼pp中使用new來(lái)創(chuàng)建一個(gè)Foo的對(duì)象,這個(gè)過(guò)程在compiler中會(huì)解釋成右邊的形式,也就是調(diào)用operator new函數(shù)來(lái)分配內(nèi)存,然后調(diào)用構(gòu)造函數(shù),創(chuàng)建對(duì)象。operator new又會(huì)去調(diào)用下一級(jí)的::operator new()函數(shù),這個(gè)是一個(gè)全局的函數(shù)。有些地方也將他們叫做操作符,因?yàn)樗麄兒?-*/的重載寫(xiě)法一樣,叫法不是我們所要去深究的。這里需要注意:
1. operator new和::operator new都是可以重載的;
2. 如果在類中重載了operator new,就會(huì)去調(diào)用我們類中的operator new, 然后才是調(diào)用到全局的::operator new();
3. 我們也可以在類中的operator new或者全局的operator new中不去調(diào)用系統(tǒng)提供的接口,這樣這個(gè)類就沒(méi)辦法在堆中申請(qǐng)內(nèi)存了;
理解了上述幾點(diǎn),我們就可以看出,實(shí)際上我們重載的這些 相當(dāng)于是hook了一些操作。
除了在app中直接使用new,我們還可以使用C++提供給我們的allocater, 這也是STL中容器使用的內(nèi)存管理工具了。下圖找你個(gè)就是allocator的調(diào)用流程。因?yàn)閍llocator是一個(gè)提供的工具,所以它里面是直接使用全局的::operator new的。

從上面的兩個(gè)圖中我們可以看出:
- 一旦我們重載了全局的::operator new,那么這個(gè)程序中所有的類,以及所有使用new的地方都會(huì)走到我們重載的那個(gè)全局::operator new中,這個(gè)影響是非常大的,也是在程序設(shè)計(jì)中很少直接這樣用的一點(diǎn)。
- 在類中重載operator new, 只會(huì)影響這個(gè)類的操作,所以一般情況下,這種方式是在內(nèi)存管理中常用的。
2. 例子
首先,我們來(lái)重載全局::operator new 和::operator delete,然后看看是不是如我們之前所說(shuō)的調(diào)用流程一致。
#include <iostream>
using namespace std;
void* myAlloc(size_t size)
{
return malloc(size);
}
void myFree(void* ptr)
{
return free(ptr);
}
inline void* operator new(size_t size)
{
cout << " global new() " << endl;
return myAlloc(size);
}
inline void* operator new[](size_t size)
{
cout << " global new[]() " << endl;
return myAlloc(size);
}
inline void operator delete(void* ptr)
{
cout << " global delete() " << endl;
return myFree(ptr);
}
inline void operator delete[](void* ptr)
{
cout << " global delete[]() " << endl;
return myFree(ptr);
}
int main()
{
int *pA = new int(10);
delete pA;
int* pArr = new int[20];
delete[] pArr;
return 0;
}
驗(yàn)證了全局的重載之后,我們?cè)陬愔兄剌doperator new和operator delete,看看在類中重載的調(diào)用流程是怎樣的。
#include <iostream>
using namespace std;
void* myAlloc(size_t size)
{
return malloc(size);
}
void myFree(void* ptr)
{
return free(ptr);
}
// 全局重載new()
inline void* operator new(size_t size)
{
cout << " global new() " << endl;
return myAlloc(size);
}
// 全局重載new[]
inline void* operator new[](size_t size)
{
cout << " global new[]() " << endl;
return myAlloc(size);
}
// 全局重載delete
inline void operator delete(void* ptr)
{
cout << " global delete() " << endl;
return myFree(ptr);
}
// 全局重載delete[]
inline void operator delete[](void* ptr)
{
cout << " global delete[]() " << endl;
return myFree(ptr);
}
class Foo
{
public:
Foo() {
cout << "foo construct" << endl;
}
~Foo() {
cout << "foo deconstruct" << endl;
}
// 類中重載 new
void* operator new(size_t size)
{
cout << "class member new" << endl;
return ::operator new(size);
}
// 類中重載 delete
void operator delete(void* ptr)
{
cout << "class member delete" << endl;
return ::operator delete(ptr);
}
// 類中重載new[]
void* operator new[](size_t size)
{
cout << "class member new[]" << endl;
return ::operator new[](size);
}
// 類中重載 delete[]
void operator delete[](void* ptr)
{
cout << "class member delete[]" << endl;
return ::operator delete[](ptr);
}
};
int main()
{
//int *pA = new int(10);
//delete pA;
//int* pArr = new int[20];
//delete[] pArr;
//Foo *foo = new Foo;
//delete foo;
Foo *foo = new Foo[10];
delete[] foo;
return 0;
}
- 在類中重載operator new[]和operator delete[]
- 重載new() 和 delete()
class Screen {
public:
Screen(int x) :i(x) {};
int geti() { return i; };
void* operator new(size_t size) {
Screen *p;
if (!freeStore) {
size_t chunk = screenChunk * size;
freeStore = p = reinterpret_cast<Screen*>(new char[chunk]);
for (; p != &freeStore[screenChunk - 1]; ++p) {
p->next = p + 1;
}
p->next = 0;
}
p = freeStore;
freeStore = freeStore->next;
return p;
}
void operator delete(void* ptr, size_t) {
(static_cast<Screen*>(ptr))->next = freeStore;
freeStore = static_cast<Screen*>(ptr);
}
private:
Screen* next;
static Screen* freeStore;
static const int screenChunk;
int i;
};
Screen* Screen::freeStore = 0;
const int Screen::screenChunk = 24;