C++ new与delete
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) ", m);} ~foo() {printf("~foo() ");} int get() {printf("m=%d ", m);} static void* operator new(size_t size) { printf("operator new() "); return ::operator new(size); } static void operator delete(void* ptr) { printf("operator delete() "); ::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) ", m);} ~foo() {printf("~foo() ");} int get() { printf("m=%d ", m); } static void* operator new(size_t size, char* desc) { printf("operator new(%s) ", desc); void* p = ::operator new(size); return p; } static void operator delete(void* ptr) { printf("operator delete() "); ::operator delete(ptr); } static void operator delete(void* ptr, char* desc) { printf("operator delete(%s) ", 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) ", m); throw this; } ~foo() {printf("~foo() ");} int get() { printf("m=%d ", m); } static void* operator new(size_t size, char* desc) { printf("operator new(%s) ", desc); void* p = ::operator new(size); return p; } static void operator delete(void* ptr) { printf("operator delete() "); ::operator delete(ptr); } static void operator delete(void* ptr, char* desc) { printf("operator delete(%s) ", 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() ");} foo(int x) { m = x; printf("foo(%d) ", m); } ~foo() {printf("~foo() ");} int get() { printf("m=%d ", m); } static void* operator new(size_t size) { printf("operator new(%d) ", size); void* p = ::operator new(size); return p; } static void* operator new[](size_t size) { printf("operator new[](%d) ", size); void* p = ::operator new[](size); return p; } static void operator delete(void* ptr, size_t size) { printf("operator delete(%d) ", size); ::operator delete(ptr); } static void operator delete[](void* ptr, size_t size) { printf("operator delete[](%d) ", 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; }
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) ", m);} ~foo() {printf("~foo() ");} 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) ", m);} ~foo() {printf("~foo() ");} int get() {printf("m=%d ", 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(),不管内存分配,只管调用构造函数构建对象,此时需要手动管理内存,并单独析构;