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

Effective C++ 条款50:了解new和delete的合理替换时机

Effective C++ 条款50:了解new和delete的合理替换时机


核心思想自定义operator newoperator delete可用于优化性能、调试内存问题及获取使用统计,但需深入理解平台内存管理机制,确保实现正确性与高效性。

⚠️ 1. 替换默认new/delete的三大理由

1.1 检测内存错误

  • 诊断内存越界写入
  • 检测未配对new/delete调用
  • 识别使用未初始化内存

1.2 优化性能

  • 提升特定场景分配效率
  • 减少内存碎片
  • 实现专属内存池策略

1.3 收集使用数据

  • 统计内存使用模式
  • 分析内存分配时间分布
  • 跟踪对象生命周期

🚨 2. 自定义内存管理器的实现要点

2.1 对齐要求

  • C++11要求所有内存分配器满足基本对齐要求(alignof(std::max_align_t))
  • 自定义分配器需处理平台特定对齐需求
// 对齐感知的内存分配示例
static const int ALIGNMENT = 16;void* alignedAlloc(size_t size) {void* orig = malloc(size + ALIGNMENT - 1 + sizeof(void*));if (!orig) return nullptr;void* aligned = static_cast<char*>(orig) + sizeof(void*);size_t offset = ALIGNMENT - (reinterpret_cast<size_t>(aligned) & (ALIGNMENT - 1));aligned = static_cast<char*>(aligned) + offset;*(static_cast<void**>(aligned) - 1) = orig;return aligned;
}void alignedFree(void* aligned) {if (aligned) {void* orig = *(static_cast<void**>(aligned) - 1);free(orig);}
}

2.2 正确实现边界标记

  • 调试版本可添加边界标记检测内存越界
// 调试内存分配器示例
struct DebugInfo {size_t size;unsigned magic;  // 魔术数字检测内存损坏
};void* debugOperatorNew(size_t size) {if (size == 0) size = 1;size_t totalSize = size + sizeof(DebugInfo) * 2;char* pMem = static_cast<char*>(malloc(totalSize));// 前边界标记DebugInfo* front = reinterpret_cast<DebugInfo*>(pMem);front->size = size;front->magic = 0xDEADBEEF;// 后边界标记DebugInfo* back = reinterpret_cast<DebugInfo*>(pMem + sizeof(DebugInfo) + size);back->magic = 0xDEADBEEF;return pMem + sizeof(DebugInfo);
}void debugOperatorDelete(void* pMem) noexcept {if (!pMem) return;char* pOrig = static_cast<char*>(pMem) - sizeof(DebugInfo);DebugInfo* front = reinterpret_cast<DebugInfo*>(pOrig);// 检查前魔术数字if (front->magic != 0xDEADBEEF) {logCorruption(pMem);  // 记录内存损坏}// 检查后魔术数字DebugInfo* back = reinterpret_cast<DebugInfo*>(pOrig + sizeof(DebugInfo) + front->size);if (back->magic != 0xDEADBEEF) {logCorruption(pMem);  // 记录内存损坏}free(pOrig);
}

⚖️ 3. 高性能内存池实现策略

3.1 固定大小对象分配器

  • 针对频繁创建销毁的特定类型对象
  • 使用空闲链表管理内存块
class FixedSizeAllocator {
public:FixedSizeAllocator(size_t blockSize) : m_blockSize(blockSize) {}void* allocate() {if (m_freeList) {void* p = m_freeList;m_freeList = *(static_cast<void**>(m_freeList));return p;} else {if (m_blocks.empty() || m_currentIndex >= BLOCKS_PER_CHUNK) {allocateNewChunk();m_currentIndex = 0;}char* pMem = m_blocks.back() + (m_currentIndex * m_blockSize);m_currentIndex++;return pMem;}}void deallocate(void* p) {*(static_cast<void**>(p)) = m_freeList;m_freeList = p;}private:static const int BLOCKS_PER_CHUNK = 64;size_t m_blockSize;void* m_freeList = nullptr;std::vector<char*> m_blocks;size_t m_currentIndex = BLOCKS_PER_CHUNK;void allocateNewChunk() {char* pNew = static_cast<char*>(::operator new(BLOCKS_PER_CHUNK * m_blockSize));m_blocks.push_back(pNew);}
};// 模板特化实现类型专属分配器
template<typename T>
class TypeAllocator {
public:static void* operator new(size_t size) {if (size != sizeof(T))return ::operator new(size);  // 非标准大小使用全局newreturn s_allocator.allocate();}static void operator delete(void* p, size_t size) {if (!p) return;if (size != sizeof(T)) {::operator delete(p);return;}s_allocator.deallocate(p);}private:static FixedSizeAllocator s_allocator;
};template<typename T>
FixedSizeAllocator TypeAllocator<T>::s_allocator(sizeof(T));

💡 关键设计原则

  1. 确保线程安全
    多线程环境下的内存分配器需适当同步:

    class ThreadSafeAllocator {
    public:void* allocate(size_t size) {std::lock_guard<std::mutex> lock(m_mutex);return m_allocator.allocate(size);}void deallocate(void* p) {std::lock_guard<std::mutex> lock(m_mutex);m_allocator.deallocate(p);}private:std::mutex m_mutex;SomeAllocator m_allocator;
    };
    
  2. 处理分配失败策略
    自定义分配器应提供适当的失败处理:

    void* customOperatorNew(size_t size) {void* p = nullptr;// 多次尝试分配,可能释放备用内存后重试for (int i = 0; i < MAX_RETRY_COUNT; ++i) {p = customAllocate(size);if (p) break;// 尝试释放紧急备用内存if (!releaseEmergencyMemory()) {break;  // 无备用内存可释放}}if (!p) {// 仍失败,使用标准处理策略std::new_handler handler = std::get_new_handler();if (handler) {handler();} else {throw std::bad_alloc();}}return p;
    }
    
  3. 与系统分配器协同工作
    大型分配直接委托给系统:

    void* hybridAlloc(size_t size) {// 大块内存直接使用系统分配器if (size > LARGE_ALLOCATION_THRESHOLD) {return ::operator new(size);}// 中小块使用自定义分配器return customPoolAlloc(size);
    }
    

现代C++扩展

// C++17 对齐分配函数
void* alignedNew(size_t size, size_t alignment) {
#ifdef __cpp_aligned_newreturn operator new(size, std::align_val_t(alignment));
#else// 手动实现对齐分配return alignedAlloc(size, alignment);
#endif
}// C++14 大小感知的释放函数
void sizedDelete(void* p, size_t size) {
#ifdef __cpp_sized_deallocationoperator delete(p, size);
#elseoperator delete(p);
#endif
}

性能监控集成

class InstrumentedAllocator {
public:static void* allocate(size_t size) {auto start = std::chrono::high_resolution_clock::now();void* p = underlyingAlloc(size);auto end = std::chrono::high_resolution_clock::now();recordAllocation(size, end - start);return p;}static void deallocate(void* p, size_t size) {auto start = std::chrono::high_resolution_clock::now();underlyingDealloc(p, size);auto end = std::chrono::high_resolution_clock::now();recordDeallocation(size, end - start);}private:static std::map<void*, AllocationInfo> s_allocationMap;
};

总结
替换全局operator newoperator delete是强大的高级技术,可用于调试、性能优化和资源监控。但实现必须谨慎处理对齐、线程安全和边界情况。对于特定场景,实现类专属分配器或使用内存池通常比替换全局分配器更安全有效。现代C++提供了对齐分配、大小感知释放等特性,简化了高性能内存管理器的实现。

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

相关文章:

  • 实践项目-1
  • jenkins自动化部署
  • 七十二、【Linux数据库】MySQL数据库MHA集群概述 、 部署MHA集群
  • 当MySQL的int不够用了
  • GTSAM中实现多机器人位姿图优化(multi-robot pose graph optimization)示例
  • 权限管理系统
  • 动手学深度学习(pytorch版):第四章节—多层感知机(7、8)数值稳定性和模型初始化
  • 《算法导论》第 31 章 - 数论算法
  • 个人介绍CSDNmjhcsp
  • Kubernetes集群安装部署--flannel
  • Vue 2 项目中快速集成 Jest 单元测试(超详细教程)
  • 云计算学习100天-第23天
  • github 上传代码步骤
  • 【Python】新手入门:python模块是什么?python模块有什么作用?什么是python包?
  • Day13_【DataFrame数据组合merge连接】【案例】
  • 嵌入式开发学习———Linux环境下网络编程学习(三)
  • 第5.5节:awk算术运算
  • RabbitMQ:交换机(Exchange)
  • LeetCode-17day:贪心算法
  • 95、23种设计模式之建造者模式(4/23)
  • 大模型 + 垂直场景:搜索/推荐/营销/客服领域开发新范式与技术实践
  • 抓取手机游戏相关数据
  • 细化的 Spring Boot 和 Spring Framework 版本对应关系
  • c++计算器(简陋版)
  • 【全面推导】策略梯度算法:公式、偏差方差与进化
  • 差分(附带例题题解)
  • 深度学习 --- 基于ResNet50的野外可食用鲜花分类项目代码
  • 基于单片机身体健康监测/身体参数测量/心率血氧血压
  • 接口性能测试工具 - JMeter
  • . keepalived+haproxy