C++中的operator new与operator delete
什么是 operator new 和 operator delete
C++的 new 会做两件事:
- 调用
operator new分配内存(实际上会调用malloc函数)。 - 调用构造函数构造对象。
比如:
Complex *c = new Complex(1, 2);
上面的构造函数会被编译器转化为:
void *mem = operator new(sizeof(Complex));
c = static_cast<Complex*>(mem);
c->Complex::Complex(1, 2);
delete 也会做两件事:
- 调用析构函数。
- 调用
operator delete释放内存(实际上会调用free函数)。
比如:
Complex *c = new Complex(1, 2);
delete c;
上面的 delete 会被编译器转化为:
Complex::~Complex(c);
operator delete(c);
重载 operator new 和 operator delete
我们可以重载 operator new 和 operator delete 来接管系统默认的分配内存和释放内存的行为。
有两种重载方式:
1. 全局重载
重载全局的 ::operator new 、 ::operator delete 、 ::operator new[] 、 ::operator delete[] 。
比如:
void *mymalloc(size_t size) {
return malloc(size);
}
void myfree(void *ptr) {
return free(ptr);
}
void *operator new(size_t size) {
std::cout << "customize global operator new" << std::endl;
return mymalloc(size);
}
void *operator new[](size_t size) {
std::cout << "customize global operator new[]" << std::endl;
return mymalloc(size);
}
void operator delete(void *ptr) {
std::cout << "customize global operator delete" << std::endl;
return myfree(ptr);
}
void operator delete[](void *ptr) {
std::cout << "customize global operator delete[]" << std::endl;
return myfree(ptr);
}
重载之后,它的影响是全局性的,所有的 operator new 、 operator delete 、 operator new[] 、 ::operator delete[] 都会走我们重载之后的函数。要慎用。
2. 类内重载
要想单独的控制某一个类的内存分配与释放的行为,应该在类内重载 operator new 、 operator delete 、 operator new[] 、 ::operator delete[] 。
比如:
class Foo {
public:
void *operator new(size_t size) {
std::cout << "customize member operator new" << std::endl;
return mymalloc(size);
}
void *operator new[](size_t size) {
std::cout << "customize member operator new[]" << std::endl;
return mymalloc(size);
}
void operator delete(void *ptr) {
std::cout << "customize member operator delete" << std::endl;
return myfree(ptr);
}
void operator delete[](void *ptr) {
std::cout << "customize member operator delete[]" << std::endl;
return myfree(ptr);
}
};
placement new
在类内也可以重载 operator new ,写出多个版本,前提是它们必须具有不同的参数列,其中第一个参数列的类型必须是 size_t ,其余参数以 new 关键字所指定的 place arguments 为初值。出现在 new() 中的参数便是 place arguments ,比如:
Foo *f = new(100, a) Foo(5);
上面 new(100, a) 中的 100、a 都是 place arguments 。这种带有参数的 new 称为 placement new 。
在类内也可以重载多个 operator delete ,但它们不会被 delete 所调用,只有在 new 所调用的构造函数抛出异常时,才会调用相应的重载版本的 operator delete ,来释放未成功创建的对象的内存。但是,下面的 Foo *f5 = new(100) Foo(1) 会调用一个抛出异常的构造函数,相应的重载版本的 operator delete 却没有被调用,为什么?没搞明白。
class Foo {
public:
Foo() : m_i() {
std::cout << "default constructor Foo()" << std::endl;
}
explicit Foo(int i) : m_i(i) {
std::cout << "customize constructor Foo(int)" << std::endl;
throw std::exception();
}
// ①
void *operator new(size_t size) {
std::cout << "operator new(size_t size)" << std::endl;
return malloc(size);
}
// ①
void operator delete(void *ptr, size_t size) {
std::cout << "operator delete(void *ptr, size_t size)" << std::endl;
}
// ②
void *operator new(size_t size, void *start) {
std::cout << "operator new(size_t size, void *start)" << std::endl;
return start;
}
// ②
void operator delete(void *ptr, void *start) {
std::cout << "operator delete(void *ptr, void *start)" << std::endl;
}
// ③
void *operator new(size_t size, long extra) {
std::cout << "operator new(size_t size, long extra)" << std::endl;
return malloc(size + extra);
}
// ③
void operator delete(void *ptr, long extra) {
std::cout << "operator delete(void *ptr, long extra)" << std::endl;
}
// ④
void *operator new(size_t size, long extra, char init) {
std::cout << "operator new(size_t size, long extra, char init)" << std::endl;
return malloc(size + extra);
}
// ④
void operator delete(void *ptr1, long extra, char init) {
std::cout << "operator delete(void *ptr1, long extra, char init)" << std::endl;
}
private:
int m_i;
};
int main() {
Foo start;
std::cout << std::endl;
Foo *f1 = new Foo;
std::cout << std::endl;
Foo *f2 = new(&start) Foo;
std::cout << std::endl;
Foo *f3 = new(100) Foo;
std::cout << std::endl;
Foo *f4 = new(100, 'a') Foo;
std::cout << std::endl;
Foo *f5 = new(100) Foo(1);
std::cout << std::endl;
return 0;
}
一些小问题
1. 在使用 new 创建一个数组时,传给 void *operator new[](size_t size) 的参数到底是多大?
比如,现在有一个类 Foo ,使用 sizeof(Foo) 查看其内存大小为 48 字节,现在创建一个大小为 5 的 Foo 数组。
class Foo {
public:
int a;
double b;
std::string c;
void *operator new[](size_t size) {
std::cout << "size = " << size << std::endl;
return malloc(size);
}
};
int main() {
std::cout << sizeof(Foo) << std::endl;
Foo *array = new Foo[5];
return 0;
}
传入 void *operator new[](size_t size) 的 size 大小为 48 * 5 + 8,这多出来的 8 个字节,用来存放这块数组的大小是 5 。如下图所示:

2. operator new 和 operator delete 的调用顺序?
先尝试调用类内重载的 operator new 和 operator delete 。
再尝试调用全局的 operator new 和 operator delete 。
也可以显式的指定调用全局的 operator new 和 operator delete ,比如下面这样:
Foo *f1 = ::new Foo;

浙公网安备 33010602011771号