当前位置: 首页 > news >正文

Effective C++ 条款51:编写new和delete时需固守常规

Effective C++ 条款51:编写new和delete时需固守常规


核心思想实现自定义的operator newoperator 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;
}

💡 关键设计原则

  1. 保证与全局版本的一致性
    自定义版本应提供与全局版本相同的保证:

    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);}
    };
    
  2. 数组版本的实现
    如需处理数组分配,需实现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);
    }
    
  3. 对齐处理
    现代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 newoperator delete需要严格遵守C++标准规定的行为准则,包括正确处理边界情况(零字节请求、空指针删除)、与new-handler机制正确交互、保证异常安全,以及确保分配与释放函数之间的正确匹配。类专属版本应处理继承场景,确保派生类能正确使用基类的分配机制。现代C++还增加了对齐分配和大小感知删除等特性,实现时需考虑这些进阶要求。通过遵循这些规范,可以创建出既高效又安全的自定义内存管理系统。

http://www.dtcms.com/a/339244.html

相关文章:

  • Pandas 入门到实践:核心数据结构与基础操作全解析(Day1 学习笔记)
  • 电源、电流及功率实测
  • Shader开发(十五)创建四边形
  • 【工作笔记】VMware安装 - 安装程序检测到主机启用了Hyper-V或Device/Credential Guard……提示解决方法
  • 在CentOS系统中查询已删除但仍占用磁盘空间的文件
  • 深入解析:Unity、Unreal Engine与Godot引擎中的Uniform变量管理
  • 【CV 图像分割】①——目标分割介绍
  • 网络编程day4
  • 牛客面经 - 2025/8/19
  • C++高频知识点(二十八)
  • 数据库-MYSQL配置下载
  • 前端性能优化实战手册:从网络到运行时,一套可复制落地的清单
  • 基于提示词工程和MCP构建垂直Agent应用
  • Go语言中的优雅并发控制:通道信号量模式详解
  • JS 中的 this
  • AI-调查研究-55-机器人 百年进化史:从Unimate到人形智能体的技术跃迁
  • Navicat 使用超详细教程:从下载到实战案例
  • Vue.prototype 的作用
  • AJAX (一)
  • 【深度学习-pytorch】mnist数字识别
  • Java 大视界 -- Java 大数据机器学习模型在自然语言处理中的多语言翻译与文化适应性优化
  • go.uber.org/zap 日志库高性能写入
  • 结合BI多维度异常分析(日期-> 商家/渠道->日期(商家/渠道))
  • 常见BI工具
  • 变电站智能辅助监控系统:结构框架、功能模块及配套设备指南
  • 【国内电子数据取证厂商龙信科技】Python数据分析环境搭建
  • 科技云报到:AI推理破局,金融服务如何“逆天改命”
  • JavaWeb开发笔记合集
  • 工厂MES管理系统的五大核心应用场景
  • 功能上新:燕千云ITSM如何让高频重复问题自动总结推送