Item 50:为什么需要自定义new和delete?

Item 50: Understand when it makes sense to replace new and delete.

我们在Item 49中介绍了如何自定义new的错误处理函数,以及如何为你的类重载operator new。 现在我们回到更基础的问题,为什么我们需要自定义operator new或operator delete?

  • 检测使用错误。new得到的内存如果没有delete会导致内存泄露,而多次delete又会引发未定义行为。如果自定义operator new来保存动态内存的地址列表,在delete中判断内存是否完整,便可以识别使用错误,避免程序崩溃的同时还可以记录这些错误使用的日志。
  • 提高效率。全局的new和delete被设计为通用目的(general purpose)的使用方式,通过提供自定义的new,我们可以手动维护更适合应用场景的存储策略。
  • 收集使用信息。在继续自定义new之前,你可能需要先自定义一个new来收集地址分配信息,比如动态内存块大小是怎样分布的?分配和回收是先进先出FIFO还是后进先出LIFO?
  • 实现非常规的行为。比如考虑到安全,operator new把新申请的内存全部初始化为0.
  • 其他原因,比如抵消平台相关的字节对齐,将相关的对象放在一起等等。

自定义一个operator new很容易的,比如实现一个支持越界检查的new:

static const int signature = 0xDEADBEEF;    // 边界符
typedef unsigned char Byte; 

void* operator new(std::size_t size) throw(std::bad_alloc) {
    // 多申请一些内存来存放占位符 
    size_t realSize = size + 2 * sizeof(int); 

    // 申请内存
    void *pMem = malloc(realSize);
    if (!pMem) throw bad_alloc(); 

    // 写入边界符
    *(reinterpret_cast<int*>(static_cast<Byte*>(pMem)+realSize-sizeof(int))) 
        = *(static_cast<int*>(pMem)) = signature;

    // 返回真正的内存区域
    return static_cast<Byte*>(pMem) + sizeof(int);
}

其实上述代码是有一些瑕疵的:

  • Item 49提到operator new应当不断地调用new handler,上述代码中没有遵循这个惯例;
  • 有些体系结构下,不同的类型被要求放在对应的内存位置。比如double的起始地址应当是8的整数倍,int的起始地址应当是4的整数倍。上述代码可能会引起运行时硬件错误。
  • 起始地址对齐。C++要求动态内存的起始地址对所有类型都是字节对齐的,new和malloc都遵循这一点,然而我们返回的地址偏移了一个int。

到此为止你已经看到了,实现一个operator new很容易,但实现一个好的operator new却很难。其实我们还有别的选择:比如去读编译器文档、内存管理的商业工具、开源内存管理工具等。

转载地址:http://harttle.land/2015/09/19/effective-cpp-50.html
感谢作者 Harttle

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
deletedelete\[\]是用来释放动态分配的内存的操作符。它们之间的区别在于,delete用于释放通过new操作符分配的单个对象的内存,而delete\[\]用于释放通过new\[\]操作符分配的数组对象的内存。\[1\] 在使用delete\[\]时,它需要知道内存中元素的数量,以便逐个调用析构函数。这个数量并不是通过delete\[\]传递的,而是在使用new\[\]时保存的。因此,delete\[\]可以根据元素数量来释放内存。\[1\] 对于基本类型的指针,如int *ptr = new int\[5\]和A *ptr = new A\[5\],deletedelete\[\]的操作都只是简单地释放传入的地址,不会有其他额外的操作。因此,在这种情况下,使用deletedelete\[\]都是可以的,不会出现问题。\[2\] 然而,对于自定义类型,情况会有所不同。如果自定义类型中有指针成员,使用delete来释放内存是不正确的,因为它无法调用指针成员的析构函数。在这种情况下,应该使用delete\[\]来释放内存,以确保所有的析构函数都被正确调用。\[3\] 综上所述,deletedelete\[\]的使用是根据内存分配的方式和对象类型来决定的。对于单个对象的动态分配,使用delete;对于数组对象的动态分配,使用delete\[\]。对于自定义类型,如果有指针成员,应该使用delete\[\]来释放内存。 #### 引用[.reference_title] - *1* *2* [C++deletedelete []的真正区别](https://blog.csdn.net/u011555996/article/details/128428224)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [deletedelete[]的区别](https://blog.csdn.net/qq_45523399/article/details/126535331)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值