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没用了吗?
new能够完全替代malloc吗?绝大部分情况下,答案都是肯定的。但是有一种情况则非用malloc不可了。根据new的定义,其第一个参数是待分配对象的大小,但在使用时不需要明确地给出这个值。这个值是由编译器暗中替你完成的。倘若在某种情况下,需要在分配一个对象的同时还要分配出一些额外的空间用来管理某些相关的信息。这个额外空间与对象的空间要求连续。这个时候new就帮不上了。必须用malloc把对象和额外空间的总大小作为malloc的参数。在分配出来了后,可能需要调用new的放置形式的调用在该块内存上构造对象。
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] 将这个对象当作原始内存(即不调用析构函数)。