Cpp Operators of new and delete
1. 動態內存分配與釋放(new and delete)
#include <new>
void* operator new(size_t); // 參數是單個對象的大小
void* operator new[](size_t); // 參數是對象數組的總的大小
void delete(void*);
void delete[](void*);
char* save_string(const char* p)
char* s = new char[strlen(p)+1];
// ...
return s;
char* p = save_string(argv[1]);
// ...
delete[] p;
class X { /* ... */ }
X* p = new[10] X;
X* p2 = new X[10];
vector<int>* pv = new vector<int>(5);
2. 提供自己的內存管理:重載new/delete操作符
一個類的operator new()和operator delete()成員函數,隱式地成爲靜態成員函數。因此它們沒有this指針,也不能修改對象(很好理解,當調用new的時候對象還沒有真正創建呢,當然不能修改對象了!)。當然在重載定義的時候,原型還是要與前面提到的一致。看下面這個例子:
void* Employee::operator new(size_t s)
// 分配s字節的內存空間,並返回這個空間的地址
void Employee::operator delete(void* p, size_t s)
// 假定指針p是指向由Employee::operator new()分配的大小爲s字節的內存空間。
// 釋放這塊空間以供系統在以後重用。
任何一個operator new()的操作符定義,都以一個尺寸值作爲第一個參數,且待分配對象的大小隱式給定,其值就作爲new操作符函數的第一個參數值。
class Employee {
void* operator new[](size_t);
void operator delete[](void*); // 單參數形式,少了一個size_t參數
void operator delete[](void*,size_t); //兩個參數形式也是可以的,但無必要
// ...
在編譯器的內部實現中,傳入new/delete[]的尺寸值可能是數組的大小s加上一個delta。這個delta量是編譯器的內部實現所定義的某種額外開銷。爲什麼delete操作符不需要第二個尺寸參數呢?因爲這個數組的大小s以及delta量都由系統“記住”了。但是delete[]的兩個參數形式的原型也是可以聲明的,在調用的時候會把s*sizeof(SomeClass)+delta作爲第二個參數值傳入。delta量是與編譯器實現相關的,因此對於用戶程序員來說是不必要知道的。故而這裏只提供單參數版本就可以了。(這倒是提供了一種查看這個delta量的方法。根據實際測試,GCC 4.1採用了4個字節的delta量。)
到這裏應該注意到,當我們調用operator delete()的時候,只給出了指針,並沒有給出對象大小的參數。那麼編譯器是怎麼知道應該給operator delete()提供正確的尺寸值的呢?如果delete參數類型就是該對象的確切型別,那麼這是一個簡單的事情,但是事情並不是總是這樣。看下面的例子:
class Manager : public Employee {
int level;
// ...
void f()
Employee* p = new Manager; // 麻煩:確切型別丟失了!
delete p;
3. 在指定位置安放對象(Placement of Objects)
class X {
void* operator new(size_t, void* p) { return p; } // 顯示安放操作符
void* buf = reinterpret_cast<void*>(0xF00F); // 某個重要的地址
X* p2 = new(buf) X; // 在buf地址處創建一個X對象,
// 實際調用函數operator new(sizeof(X),buf)
4. 內存分配失敗與new_handler
void f()
for(;;) new char [10000];
catch(bad_alloc) {
cerr << "Memory exhausted!/n";
#include <new> // set_new_handler()原型在此頭文件中
void out_of_store()
cerr << "operator new failed: out of store/n";
throw bad_alloc();
for(;;) new char[10000];
cout << "done/n";
operator new failed: out of store
typedef void (*new_handler)();
5. 標準頭文件<new>中的原型
class bad_alloc : public exception { /* ... */ }
struct nothrow_t { };
extern struct nothrow_t nothrow; // 內存分配器將不會拋出異常
typedef void (*new_handler)();
new_handler set_new_handler(new_handler new_p) throw();
// 單個對象的分配與釋放
void* operator new(size_t) throw(bad_alloc);
void operator delete(void*) throw();
// 對象數組分配與釋放
void* operator new[](size_t) throw(bad_alloc);
void operator delete[](void*) throw();
// 單個對象分配與釋放
void* operator new(size_t, const nothrow_t&) throw();
void operator delete(void*, const nothrow_t&) throw();
// 對象數組分配與釋放
void* operator new[](size_t, const nothrow_t&) throw();
void operator delete[](void*, const nothrow_t&) throw();
// 分配已有空間給單個對象使用
void* operator new(size_t, void* p) throw() { return p; }
void operator delete(void* p, void*) throw() { } //什麼都不做!
// 分配已有空間給對象數組使用
void* operator new[](size_t, void* p) throw() {return p;}
void operator delete[](void* p, void*) throw() { } //什麼也不做!
class X {
public:
X(int n){};
// ...
X* p = new X;
X* p1 = new X(5);
X* pa = new X[10];
X* pa2 = new[20] X(5);
原型的第二個參數要求一個nothrow_t的引用,因此必須以<new>中定義的nothrow全局對象作爲new/delete的參數,如下所示:
void f()
int* p = new int[10000]; // 可能會拋出bad_alloc異常
if(int* q = new(nothrow) int[100000]; {
// 內存分配成功
else {
// 內存分配失敗
6. new與異常
void f(Arena& a, X* buffer)
X* p1 = new X;
X* p2 = new X[10];
X* p3 = new(buffer[10]) X;
X* p4 = new(buffer[11]) X[10];
X* p5 = new(a) X;
X* p6 = new(a) X[10];
7. malloc/free沒用了嗎?
8. 垃圾收集
void f()
int* p = new int;
long i1 = reinterpret_cast<long>(p) & 0xFFFF0000;
long i2 = reinterpret_cast<long>(p) & 0x0000FFFF;
p = 0;
// 這裏就不存在指向那個整型數的指針了!
p = reinterpret_cast<int*>(i1|i2);
// 現在這個整型數又被引用了!
union U {
int* p;
int i;
void f(U u, U u2, U u3)
u.p = new int;
u2.i = 99999;
u.i = 8;
// ...
delete p;
[1] 爲這個對象調用析構函數(如果有的話);
[2] 將這個對象當作原始內存(即不調用析構函數)。