Effective C++ 条款51:编写new和delete时需固守常规
Effective C++ 条款51:编写new和delete时需固守常规
核心思想:实现自定义的operator new
和operator delete
时,必须遵循C++标准规定的行为规范,包括正确处理零字节请求、与new-handler机制协作、保证异常安全,以及确保与对应分配/释放函数的一致性。
⚠️ 1. operator new的实现准则
1.1 基本要求:
- 循环尝试分配内存,失败时调用new-handler
- 处理零字节请求(返回合法指针)
- 避免掩盖全局版本的operator new
1.2 标准合规实现:
void* customOperatorNew(std::size_t size) {if (size == 0) size = 1; // 处理零字节请求while (true) {void* pMem = std::malloc(size); // 尝试分配if (pMem) return pMem; // 成功则返回// 失败时调用当前new-handlerstd::new_handler handler = std::get_new_handler();if (!handler) throw std::bad_alloc(); // 无handler则抛出异常handler(); // 调用handler可能释放内存或采取其他措施}
}// 不抛出异常版本(nothrow)
void* customOperatorNew(std::size_t size, const std::nothrow_t&) noexcept {try {return customOperatorNew(size);} catch (const std::bad_alloc&) {return nullptr;}
}
🚨 2. operator delete的实现准则
2.1 基本要求:
- 安全处理空指针删除
- 与对应operator new的实现匹配
- 保证异常安全(绝不抛出异常)
2.2 标准合规实现:
void customOperatorDelete(void* pMem) noexcept {if (!pMem) return; // 安全处理空指针std::free(pMem); // 释放内存
}// 大小感知版本(C++14)
void customOperatorDelete(void* pMem, std::size_t size) noexcept {if (!pMem) return;// 可使用size信息优化释放策略(void)size; // 避免未使用参数警告std::free(pMem);
}
⚖️ 3. 类专属版本的实现要点
3.1 自定义类分配器:
- 用于优化特定类的内存管理
- 通常基于内存池或专用分配策略
class Widget {
public:static void* operator new(std::size_t size);static void operator delete(void* pMem, std::size_t size) noexcept;private:struct WidgetBlock { // 内存块结构WidgetBlock* next;};static WidgetBlock* freeList; // 空闲链表static const int BLOCK_COUNT = 64; // 每次分配的块数
};// 初始化静态成员
Widget::WidgetBlock* Widget::freeList = nullptr;void* Widget::operator new(std::size_t size) {// 确保大小正确(可能涉及继承)if (size != sizeof(Widget)) {return ::operator new(size);}if (!freeList) {// 分配新批次内存块freeList = static_cast<WidgetBlock*>(::operator new(BLOCK_COUNT * sizeof(Widget)));// 构建空闲链表for (int i = 0; i < BLOCK_COUNT - 1; ++i) {freeList[i].next = &freeList[i + 1];}freeList[BLOCK_COUNT - 1].next = nullptr;}// 分配一个块WidgetBlock* block = freeList;freeList = freeList->next;return block;
}void Widget::operator delete(void* pMem, std::size_t size) noexcept {if (!pMem) return;// 确保大小正确if (size != sizeof(Widget)) {::operator delete(pMem);return;}// 将块返回空闲链表WidgetBlock* block = static_cast<WidgetBlock*>(pMem);block->next = freeList;freeList = block;
}
💡 关键设计原则
-
保证与全局版本的一致性
自定义版本应提供与全局版本相同的保证:class Base { public:// 自定义operator newstatic void* operator new(std::size_t size) {if (size != sizeof(Base)) {// 处理继承场景:派生类可能更大return ::operator new(size);}return customAllocate(size);}// 必须提供匹配的operator deletestatic void operator delete(void* pMem, std::size_t size) noexcept {if (!pMem) return;if (size != sizeof(Base)) {::operator delete(pMem);return;}customDeallocate(pMem);} };
-
数组版本的实现
如需处理数组分配,需实现operator new[]
和operator delete[]
:void* operator new[](std::size_t size) {return customOperatorNew(size); }void operator delete[](void* pMem) noexcept {customOperatorDelete(pMem); }void operator delete[](void* pMem, std::size_t size) noexcept {customOperatorDelete(pMem, size); }
-
对齐处理
现代C++需确保适当的内存对齐:#include <cstddef> #include <new>void* alignedAlloc(std::size_t size, std::size_t alignment) {// C++17起可使用std::align_val_t #if __cpp_aligned_newreturn operator new(size, std::align_val_t(alignment)); #else// 手动实现对齐分配void* pOrig = std::malloc(size + alignment - 1 + sizeof(void*));if (!pOrig) return nullptr;void* pAligned = static_cast<char*>(pOrig) + sizeof(void*);std::size_t offset = alignment - (reinterpret_cast<std::size_t>(pAligned) & (alignment - 1));pAligned = static_cast<char*>(pAligned) + offset;// 存储原始指针以便释放*(static_cast<void**>(pAligned) - 1) = pOrig;return pAligned; #endif }void alignedFree(void* pMem) noexcept { #if __cpp_aligned_newoperator delete(pMem); // 编译器处理对齐释放 #elseif (pMem) {void* pOrig = *(static_cast<void**>(pMem) - 1);std::free(pOrig);} #endif }
调试与诊断支持:
#ifdef DEBUG_MEMORY void* debugOperatorNew(std::size_t size, const char* file, int line) {void* pMem = customOperatorNew(size);recordAllocation(pMem, size, file, line);return pMem; }void debugOperatorDelete(void* pMem, const char* file, int line) noexcept {recordDeallocation(pMem, file, line);customOperatorDelete(pMem); }// 使用宏简化调试版本 #define DEBUG_NEW new(__FILE__, __LINE__) #define delete delete(__FILE__, __LINE__), delete #endif
性能优化技巧:
class HighPerformanceAllocator { public:static void* operator new(std::size_t size) {// 线程局存储避免锁竞争thread_local MemoryPool pool;return pool.allocate(size);}static void operator delete(void* pMem, std::size_t size) noexcept {thread_local MemoryPool pool;pool.deallocate(pMem, size);} };
总结:
实现自定义的operator new
和operator delete
需要严格遵守C++标准规定的行为准则,包括正确处理边界情况(零字节请求、空指针删除)、与new-handler机制正确交互、保证异常安全,以及确保分配与释放函数之间的正确匹配。类专属版本应处理继承场景,确保派生类能正确使用基类的分配机制。现代C++还增加了对齐分配和大小感知删除等特性,实现时需考虑这些进阶要求。通过遵循这些规范,可以创建出既高效又安全的自定义内存管理系统。