new operator 和 delete operator
new operator 和delete operator 是运算符
,
我们知道new运算符会干2件事:申请内存和调用对象构造函数,比如,当我们new一个string对象:
string *ps = new string("Hands up!")
编译器实际的工作大概是这样:
void *mem = operator new(sizeof(string)); //申请内存
call string::string("Hands up!") on *mem; // 调用构造函数
string *ps = static_cast<string*>(mem); // 返回string*指针
同样,delete运算符也会干2件事:调用对象析构函数和释放内存
delete ps
编译器实际的工作大概是这样:
ps->~string()
operator delete(ps)
编译器看到类类型的new或者delete运算符的时候,首先查看该类是否是有operator new或者operator delete成员,如果类定义了自己的operator new和operator delete函数,则使用这些函数为对象分配和释放内存,否则调用标准库版本(:: operator new和:: operator delete)。
一个例子:
struct foo {
foo(int x) {m = x; printf("foo(%d)\n", m);}
~foo() {printf("~foo()\n");}
int get() {printf("m=%d\n", m);}
static void* operator new(size_t size) {
printf("operator new()\n");
return ::operator new(size);
static void operator delete(void* ptr) {
printf("operator delete()\n");
::operator delete(ptr);
private:
int m;
int main() {
foo* f = new foo(10);
f->get();
delete f;
return 0;
operator new()
foo(10)
m=10
~foo()
operator delete()
可见,当调用new operator的时候,会先调用operator new()分配内存,然后调用构造函数;当调用delete operator的时候,先调用析构函数,然后调用operator delete()释放内存。
operator new() 和 operator delete()
operator new()和operator delete() 是函数,跟operator =() 类似。
void *operator new(size_t);
void *operator new[](size_t);
void operator delete(void*);
void operator delete[](void*);
前面提到,全局的::operator new() 内部其实就是调用malloc分配内存,而::operator delete() 内部就是调用free释放内存。
operator new() 和operator delete() 都可以重载。
重载operator new时:
第一个参数类型必须为表达要求分配空间的大小(字节),类型为size_t,此外,还可以带其它参数;
只分配需要的空间,但不调用构造函数;当无法满足所需内存空间时,如果有new_handler,则调用new_handler;
看一个带额外参数的operator new()的例子
struct foo {
foo(int x) {m = x; printf("foo(%d)\n", m);}
~foo() {printf("~foo()\n");}
int get() {
printf("m=%d\n", m);
static void* operator new(size_t size, char* desc) {
printf("operator new(%s)\n", desc);
void* p = ::operator new(size);
return p;
static void operator delete(void* ptr) {
printf("operator delete()\n");
::operator delete(ptr);
static void operator delete(void* ptr, char* desc) {
printf("operator delete(%s)\n", desc);
::operator delete(ptr);
private:
int m;
int main() {
//foo* e = new foo(10); // Error,
foo* f = new("hello") foo(10);
f->get();
delete f;
return 0;
输出结果:
operator new(hello)
foo(10)
m=10
~foo()
operator delete()
foo* e = new foo(10) 会发生错误的原因是 member function(成员函数)的名字会覆盖外围的具有相同名字的函数。
delete f 调用的是operator delete()常规版本。
考虑一下这种情况,operator new()内存分配成功,但构造函数里面抛出异常,runtime system(运行时系统)有责任撤销 operator new() 所执行的分配。然而,runtime system并不能了解被调用的 operator new() 版本是如何工作的,所以它自己无法撤销那个分配。runtime system转而寻找一个和 operator new 持有相同数量和类型额外参数的 operator delete 版本,例如:
struct foo {
foo(int x) {
m = x; printf("foo(%d)\n", m);
throw this;
~foo() {printf("~foo()\n");}
int get() { printf("m=%d\n", m); }
static void* operator new(size_t size, char* desc) {
printf("operator new(%s)\n", desc);
void* p = ::operator new(size);
return p;
static void operator delete(void* ptr) {
printf("operator delete()\n");
::operator delete(ptr);
static void operator delete(void* ptr, char* desc) {
printf("operator delete(%s)\n", desc);
::operator delete(ptr);
private:
int m;
int main() {
try {
foo* f = new("hello") foo(10);
f->get();
delete f;
} catch(void* e) {
printf("catch exception");
return 0;
operator new(hello)
foo(10)
operator delete(hello)
这时,带额外参数的operator delete被调用了,如果没有这个额外参数版本,就发生了内存泄露。
如上,规则很简单:对一个带有额外参数的 operator new(),必须既提供常规 operator delete(用于构造过程中没有抛出 exception(异常)时),又要提供一个持有与 operator new 相同的 额外参数的版本(用于构造有异常的情况)。
再看下operator new[]和operator delete[]的例子:
struct foo {
foo() {printf("foo()\n");}
foo(int x) { m = x; printf("foo(%d)\n", m); }
~foo() {printf("~foo()\n");}
int get() { printf("m=%d\n", m); }
static void* operator new(size_t size) {
printf("operator new(%d)\n", size);
void* p = ::operator new(size);
return p;
static void* operator new[](size_t size) {
printf("operator new[](%d)\n", size);
void* p = ::operator new[](size);
return p;
static void operator delete(void* ptr, size_t size) {
printf("operator delete(%d)\n", size);
::operator delete(ptr);
static void operator delete[](void* ptr, size_t size) {
printf("operator delete[](%d)\n", size);
::operator delete[](ptr);
private:
int m;
int main() {
const int len = 3;
foo* f = new foo[len]();
for (int n=0; n<len; n++) {
f[n].get();
delete []f;
return 0;
new 失败的处理
operator new 在空间不足导致内存分配失败时:
调用全局的new_handler 类型的函数,这个函数的默认处理是抛出std::bad_alloc异常;
可以通过 set_new_handler(void(*new_handler)) 自定义new_handler函数,具体实现一般是内存碎片整理;
当operator new不能满足一个内存请求的时候,它会反复调用new_handler函数直到它发现有足够的内存可以分配;
设置全局的new_handler
void my_new_handler() {
cout << "No enough memory" << endl;
exit(1);
int main() {
set_new_handler(my_new_handler);
int *pBigDataArray = new int[100000000L];
delete [] pBigDataArray;
如果operaotr new无法为100,000,000个整数分配内存,就会调用outOfMem,也就是输出一个error信息之后程序终止(exit调用);
与operator new的重载类似,我们也可以为某个类自定义new_handler, 从而代替全局的new_handler,例如
void my_new_handler() {
cout << "No enough memory" << endl;
exit(1);
class NewHandlerHolder {
public:
explicit NewHandlerHolder(std::new_handler nh): handler(nh) {} // new-handler
~NewHandlerHolder() { std::set_new_handler(handler); }
private:
std::new_handler handler;
NewHandlerHolder(const NewHandlerHolder &) =delete;
NewHandlerHolder& operator=(const NewHandlerHolder &) =delete;
class Widget {
public:
int data;
static std::new_handler set_new_handler(std::new_handler p) throw();
static void *operator new(std::size_t size) throw(std::bad_alloc);
private:
static std::new_handler currentHandler;
std::new_handler Widget::currentHandler = NULL;
std::new_handler Widget::set_new_handler(std::new_handler p) throw() {
std::new_handler oldHandler = currentHandler;
currentHandler = p;
return oldHandler;
void *Widget::operator new(std::size_t size) throw(std::bad_alloc) {
NewHandlerHolder h(std::set_new_handler(currentHandler)); // install Widget’s
return ::operator new(size); // allocate memory
// or throw
int main() {
Widget::set_new_handler(my_new_handler);
Widget* w1 = new Widget; // if memory allocation fails, call my_new_handler
string *ps = new string; // if memory allocation fails, call the global new_handler
Widget::set_new_handler(0);
Widget *pw2 = new Widget; // if memory allocation fails, throw an exception immediately. (There is no new_handler for Widget)
这里,Widget类的operator new将会做下面的事情:
调用标准set_new_handler,参数为Widget的错误处理函数,这就将Widget的new-handler安装成为了全局的new-handler。
调用全局的operator new来执行实际的内存分配。如果分配失败,全局的operator new会触发Widget的new-handler,因为这个函数已经被安装为全局new-handler。如果全局的operator new最终不能分配内存,它会抛出bad_alloc异常。在这种情况下,Widget的operator new必须恢复原来的全局new-handler,然后传播异常。为了确保源new-handler总是能被恢复,Widget将全局new-handler作为资源来处理,使用资源管理对象来防止资源泄漏。
如果全局operator new能够为Widget对象分配足够的内存。Widget的operator new就会返回指向被分配内存的指针。管理全局new-handler的对象的析构函数会自动恢复调用Widget的operator new之前的new-handler。
placement new
placement new()是operator new()的一个重载版本,如果想在已经分配的内存中创建一个对象,使用new是不行的。
placement new允许在一个已经分配好的内存中(栈或堆中)构造一个新的对象。
new(place_address) T
new(place_address) T(initializer_list)
place_address 参数必须是一个指针,指向一块预分配好的内存;
initializer_list 提供了初始化列表(可能是空的),以便在构造新分配的对象中使用。
看个例子:
struct foo {
foo(int x) {m = x; printf("foo(%d)\n", m);}
~foo() {printf("~foo()\n");}
private:
int m;
int main() {
void* addr = malloc(sizeof(foo));
foo* p = new(addr) foo(10);
p->~foo();
free(p);
return 0;
可见,placement new不能使用delete(因为没有申请内存,placement new()没有与之对应的 placement delete())。因此,析构函数需要手动调用。
也可以使用栈内存
struct foo {
foo(int x) {m = x; printf("foo(%d)\n", m);}
~foo() {printf("~foo()\n");}
int get() {printf("m=%d\n", m);}
private:
int m;
int main() {
char mem[1024];
foo* p = new(mem) foo(10);
p->get();
p->~foo();
return 0;
new operator 既分配内存,又构建对象(通过构造函数),与之相反的是delete operator;
operator new(),只管分配内存,不构建对象(不会调用构造函数),与之相反的是operator delete();
placement new(),不管内存分配,只管调用构造函数构建对象,此时需要手动管理内存,并单独析构;