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

Effective C++读书笔记——item50(什么时候替换new和delete)

在 C++ 中,有时候需要替换编译器提供的 operator new 和 operator delete 版本。下面详细介绍替换的原因、可能遇到的问题以及合适的替换时机,并给出相应的代码示例。

1. 替换 new 和 delete 的主要原因
  • 监测使用错误:通过记录已分配地址和在分配块前后设置标志字节,可以检测内存泄漏、多次删除和数据上溢 / 下溢等错误。
  • 提升性能:编译器提供的默认版本是为多种用途设计的,采用中间路线策略。如果对程序的动态内存应用模式有充分理解,自定义版本可能运行更快且需要更少的内存。
  • 收集使用方法的统计数据:自定义版本可以方便地收集被分配区块大小、生存期、分配和释放顺序等信息。
2. 自定义 operator new 示例及问题

以下是一个简单的自定义 operator new 示例,用于检测数据上溢和下溢:

#include <iostream>
#include <new>

static const int signature = 0xDEADBEEF;
typedef unsigned char Byte;

// 此代码存在缺陷
void* operator new(std::size_t size) throw(std::bad_alloc) {
    using namespace std;
    size_t realSize = size + 2 * sizeof(int);  // 增加请求大小以容纳标志字节
    void *pMem = std::malloc(realSize);        // 调用 malloc 获取内存
    if (!pMem) throw std::bad_alloc();

    // 在内存的开头和结尾写入标志字节
    *(static_cast<int*>(pMem)) = signature;
    *(reinterpret_cast<int*>(static_cast<Byte*>(pMem) + realSize - sizeof(int))) = signature;

    // 返回指向第一个标志字节之后的内存指针
    return static_cast<Byte*>(pMem) + sizeof(int);
}

该代码存在一些问题,例如没有遵循 operator new 的 C++ 惯例(如未包含调用 new-handling function 的循环),更微妙的问题是可能存在排列对齐问题。

3. 排列对齐问题

很多计算机架构要求特定类型的数据放置在具有特定性质的地址中,不遵守约束可能导致运行时硬件异常或性能下降。C++ 要求 operator new 返回适合任何数据类型排列的指针,上述代码返回的指针偏移了一个 int 大小,可能导致对齐不恰当。

4. 替换 new 和 delete 的合适时机
  • 监测使用错误:记录已分配地址和检查标志字节,检测内存泄漏、多次删除和数据上溢 / 下溢等错误。
  • 收集使用统计数据:收集被分配区块大小、生存期、分配和释放顺序等信息。
  • 提升分配和回收速度:通用目的的分配器通常比自定义版本慢,特别是针对特定类型对象专门设计的自定义版本。单线程程序可以通过编写非线程安全分配器获得速度提升,但需确定这些函数是真正的瓶颈。
// 简单的单线程固定大小分配器示例
#include <vector>

template <typename T>
class FixedSizeAllocator {
private:
    std::vector<T*> freeList;

public:
    void* allocate() {
        if (freeList.empty()) {
            return ::operator new(sizeof(T));
        }
        void* p = freeList.back();
        freeList.pop_back();
        return p;
    }

    void deallocate(void* p) {
        freeList.push_back(static_cast<T*>(p));
    }
};
  • 减少缺省内存管理的空间成本:通用目的的内存管理器通常比自定义版本使用更多的内存,针对小对象调谐的分配器可以消除这种成本。
  • 调整缺省分配器不适当的排列对齐:某些编译器提供的 operator new 可能不能保证特定类型(如 double)的动态分配按照要求的字节对齐,替换为保证对齐的版本可以提升性能。
  • 聚集相关的对象:使用 new 和 delete 的定位版本(placement versions),为特定数据结构创建独立的堆,降低页错误频率。
  • 获得不同寻常的行为:例如在共享内存中分配和回收区块,或用零覆盖被回收的内存以提高数据安全性。

总结要点

  • 替换原因多样:有很多正当理由编写 new 和 delete 的自定义版本,包括改进性能、调试堆用法错误以及收集堆用法信息。
  • 注意排列对齐:自定义 operator new 时要注意排列对齐问题,确保返回的指针适合任何数据类型的排列。
  • 考虑多种因素:在决定替换 new 和 delete 时,要综合考虑性能、内存使用、错误检测等多种因素,并进行实际测试以确定是否真的有必要替换。

相关文章:

  • 红黑树(原理)c++
  • 使用linux脚本部署discuz博客(详细注释版)
  • IMX6ULL的公板的以太网控制器(MAC)与物理层(PHY)芯片(KSZ8081RNB)连接的原理图分析(包含各引脚说明以及工作原理)
  • Cursor 入门教程与最佳实践指南
  • C#中反射的原理介绍及常见的应用场景介绍
  • ResNet 为什么能解决网络退化问题?通过图片分类案例进行验证
  • 解决前端Vue数据不更新的问题:深入分析与解决方案
  • HaProxy源码安装(Rocky8)
  • Deepseek本地部署
  • C#中的静态类以及常见用途
  • 《深度揭秘:DeepSeek如何解锁自然语言处理密码》
  • STM32创建静态库lib
  • 【每日论文】Latent Radiance Fields with 3D-aware 2D Representations
  • STL 语句表编程
  • 破解微服务疑难杂症:2025年全解决方案
  • Spring Cache 详细讲解
  • Jmeter如何计算TPS
  • JVM中的线程池详解:原理→实践
  • 代码随想录 第一章 数组 27.移除元素
  • 菜鸟之路Day15一一IO流(一)
  • “80后”萍乡市安源区区长邱伟,拟任县(区)委书记
  • 广西:坚决拥护党中央对蓝天立进行审查调查的决定
  • 云南德宏州盈江县发生4.5级地震,震源深度10千米
  • 新任美国驻华大使庞德伟抵京履职,外交部回应
  • “老中青少”四代同堂,季春艳携锡剧《玲珑女》冲击梅花奖
  • 俄副外长:俄美两国将举行双边谈判