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

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

從上面的兩個圖中我們可以看出:
- 一旦我們重載了全局的::operator new,那么這個程序中所有的類,以及所有使用new的地方都會走到我們重載的那個全局::operator new中,這個影響是非常大的,也是在程序設(shè)計中很少直接這樣用的一點。
- 在類中重載operator new, 只會影響這個類的操作,所以一般情況下,這種方式是在內(nèi)存管理中常用的。
2. 例子
首先,我們來重載全局::operator 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);
}
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;
}
驗證了全局的重載之后,我們在類中重載operator 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;