当编写自定义的 operator new
和 operator delete
时,需要遵循一些特定的惯例,以确保代码的正确性和兼容性。下面结合代码详细介绍这些惯例。
1. 自定义 operator new
的惯例
- 返回值规则:若能提供请求的内存,返回指向该内存的指针;若无法分配内存,应遵循规则抛出
std::bad_alloc
异常。 - 多次尝试分配内存:
operator new
实际上会多次尝试分配内存,每次失败后调用new-handling function
,只有当new-handling function
指针为空时才抛出异常。 - 处理零字节请求:C++ 要求即使请求零字节,
operator new
也要返回合理的指针,通常将零字节请求当作一个字节请求处理。
#include <iostream>
#include <new>void* operator new(std::size_t size) throw(std::bad_alloc) {using namespace std;if (size == 0) {size = 1; // 处理零字节请求}while (true) {void* p = std::malloc(size); // 尝试分配内存if (p) {return p; // 分配成功,返回指针}// 分配失败,获取当前的 new-handling functionnew_handler globalHandler = std::set_new_handler(0);std::set_new_handler(globalHandler);if (globalHandler) {(*globalHandler)(); // 调用 new-handling function} else {throw std::bad_alloc(); // 没有 new-handling function,抛出异常}}
}
- 处理派生类内存分配:
operator new
成员函数会被派生类继承,若基类的operator new
是为特定大小的对象调优的,当处理派生类对象分配请求时,若请求大小不符,应将请求转发给标准的operator new
。
#include <iostream>
#include <new>class Base {
public:static void* operator new(std::size_t size) throw(std::bad_alloc) {if (size != sizeof(Base)) {return ::operator new(size); // 大小不符,转发给标准 operator new}// 处理符合大小的请求return std::malloc(size);}
};class Derived : public Base {};int main() {Derived* p = new Derived; // 调用 Base::operator newdelete p;return 0;
}
operator new[]
的注意事项:若要控制数组的内存分配,需实现operator new[]
,但它只是分配裸内存,无法对数组中的对象做任何操作,也无法确定数组中对象的数量。
2. 自定义 operator delete
的惯例
- 处理空指针:C++ 保证删除空指针总是安全的,因此
operator delete
收到空指针时应什么都不做。 - 处理不同大小的删除请求:类专用的
operator delete
应检查被删除对象的大小,若大小不符,将请求转发给标准的operator delete
。
#include <iostream>
#include <new>class Base {
public:static void* operator new(std::size_t size) throw(std::bad_alloc) {if (size != sizeof(Base)) {return ::operator new(size);}return std::malloc(size);}static void operator delete(void* rawMemory, std::size_t size) throw() {if (rawMemory == 0) {return; // 处理空指针}if (size != sizeof(Base)) {::operator delete(rawMemory); // 大小不符,转发给标准 operator deletereturn;}std::free(rawMemory); // 释放内存}
};class Derived : public Base {};int main() {Derived* p = new Derived;delete p;return 0;
}
- 基类虚析构函数的重要性:若被删除的对象是从缺少虚析构函数的基类派生出来的,C++ 传递给
operator delete
的size_t
值可能不正确,因此基类应拥有虚析构函数。
总结要点
operator new
要点:包含无限循环尝试分配内存,失败时调用new-handler
,处理零字节请求,类专用版本处理大小不符的请求。operator delete
要点:收到空指针时不做处理,类专用版本处理大小不符的删除请求,基类要有虚析构函数。