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中判断内存是否完整,便可以识别使用错误,避免程序崩溃的同时还可以记录这些错误使用的日志。
  • 提高效率。全局的newdelete被设计为通用目的(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++要求动态内存的起始地址对所有类型都是字节对齐的,newmalloc都遵循这一点,然而我们返回的地址偏移了一个int

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

本文地址:http://harttle.com/2015/09/19/effective-cpp-50.html

时间: 2024-10-08 19:34:54

Item 50:为什么需要自定义new和delete?的相关文章

自定义new和delete

#include "stdafx.h" #include <stdlib.h> #include <malloc.h> #include <iostream> #include<windows.h> using namespace std; class Myclass; Myclass* x1 = NULL; Myclass* x2 = NULL; Myclass* x3 = NULL; class Myclass { public: M

Effective JavaScript Item 50 优先使用遍历方法而非循环

优先使用遍历方法而非循环 在使用循环的时候,很容易违反DRY(Don't Repeat Yourself)原则.这是因为我们通常会选择复制粘贴的方法来避免手写一段段的循环语句.但是这样做回让代码中出现大量重复代码,开发人员也在没有意义地"重复造轮子".更重要的是,在复制粘贴的时候很容易忽视循环中的那些细节,比如起始索引值,终止判断条件等. 比如以下的for循环就存在这个问题,假设n是集合对象的长度: for (var i = 0; i <= n; i++) { ... } //

50、Toast自定义布局

1 <?xml version="1.0" encoding="utf-8"?> 2 <shape xmlns:android="http://schemas.android.com/apk/res/android" > 4 <solid android:color="@color/black" /> 6 <corners android:radius="10dp"

effective c++ 思维导图

历时两个多月的时间,终于把effective c++又复习了一遍,比较慢,看的是英文版,之前看的时候做过一些笔记,但不够详细,这次笔者是从头到尾的翻译了一遍,加了一些标题,先记录到word里面,然后发布到博客园上.这么做是为了方便查阅,复习C++,同时练习英文,希望这些帖子也能够对大家有所帮助. 有人回复说有些过时了,的确,但大部分还都是effective的,可以看scott meyers关于这个问题的的回复: is effective c++ still effective? 精读一本经典书,

读书笔记 effective c++ Item 51 实现new和delete的时候要遵守约定

Item 50中解释了在什么情况下你可能想实现自己版本的operator new和operator delete,但是没有解释当你实现的时候需要遵守的约定.遵守这些规则并不是很困难,但是它们其中有一些并不直观,所以知道这些规则是什么很重要. 1. 定义operator new的约定 1.1 约定列举 我们以operator new开始.实现一个一致的operator new需要有正确的返回值,在没有足够内存的时候调用new-handling函数(见Item 49),并且做好准备处理没有内存可分配

Item 51:写new和delete时请遵循惯例

Item 51: Adhere to convention when writing new and delete. Item 50介绍了怎样自己定义new和delete但没有解释你必须遵循的惯例. 这些惯例中有些并不直观,所以你须要记住它们! operator new须要无限循环地获取资源.假设没能获取则调用"new handler".不存在"new handler"时应该抛出异常. operator new应该处理size == 0的情况. operator d

Item 52:写了placement new就要写placement delete

Item 52: Write placement delete if you write placement new "placement new"通常是专指指定了位置的new(std::size_t size, void *mem),用于vector申请capacity剩余的可用内存. 但广义的"placement new"指的是拥有额外参数的operator new. new和delete是要成对的,因为当构造函数抛出异常时用户无法得到对象指针,因而delete

Azure Stack技术深入浅出系列4: Azure Stack自定义虚拟机镜像和Gallery Item

Azure Stack环境中服务的提供者或运营者从微软变成了Azure Stack集成环境的运营者,通过Azure Stack的MarketPlace,我们可以根据用户的特定需求,提供一些定制化的应用,获得与公有云不一样的用户体验.本文将主要介绍如何在Azure Stack的MarketPlace中如何添加一个自定义的虚拟机进行并定制满足需求的Gallery Item,同时通过自服务的模式提供给业务的消费者. 这一篇,我们将探讨以下几个问题: 如何制作自定义镜像 从自定义镜像创建虚拟机的几种方式

读书笔记 effective c++ Item 54 让你自己熟悉包括TR1在内的标准库

1. C++0x的历史渊源 C++标准——也就是定义语言的文档和程序库——在1998被批准.在2003年,一个小的“修复bug”版本被发布.然而标准委员会仍然在继续他们的工作,一个“2.0版本”的C++标准预计在2009年被发布(虽然所有的工作很有可能在2007年底被完成).直到现在,发布下一版C++的预计年份还没有被确定,这就解释了为什么人们把下一版C++叫做“C++0x”——C++的200x年版本. C++0x可能会包含一些有趣的新的语言特性,但是大多数新C++功能将会以标准库附加物的形式被