#include
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
一个类的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;
class X {
void* operator new(size_t, void* p) { return p; } // 显示安放操作符
void* buf = reinterpret_cast
X* p2 = new(buf) X; // 在buf地址处创建一个X对象,
// 实际调用函数operator new(sizeof(X),buf)
void f()
for(;;) new char [10000];
catch(bad_alloc) {
cerr << "Memory exhausted!/n";
#include
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)();
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的引用,因此必须以
void f()
int* p = new int[10000]; // 可能会抛出bad_alloc异常
if(int* q = new(nothrow) int[100000]; {
// 内存分配成功
else {
// 内存分配失败
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];
void f()
int* p = new int;
long i1 = reinterpret_cast
long i2 = reinterpret_cast
p = 0;
// 这里就不存在指向那个整型数的指针了!
p = reinterpret_cast
// 现在这个整型数又被引用了!
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] 将这个对象当作原始内存(即不调用析构函数)。