Effective C++ 条款50

了解new和delete的合理替换时机

如题所述,我们在本节中只是了解一下什么时候需要自己写new和delete,为什么要重新写new和delete,出于什么样的动机等等。

本文重在论述,至于作者提供的程序代码也具有漏洞,本节的目的就是对new和delete有一个宏观的认识。

下面是替换的原因:

1.用来检测运用上的错误。如果delete new的内存失败,会导致内存泄漏。如果在new所得内存多次delete会导致不确定行为。使用编译器提供的operator new和operator delete不能检测上述行为。如果operator new持有一个链表,其存储动态分配所得内存,operator delete则将内存从链表删除,这样就能呢检测上述错误用法。如果编程错误,可能在分配内存的之前区域或之后区域写入数据;这时可以自己定义operator new分配超额内存,在多出部分写上特定byte patterns(即签名,signature),自己定义operator delete检测签名是否更改。

2.为了强化效能。 operator new和operator delete如果开辟大内存、小内存,持续这样做会造成内存碎片,这在服务器的后台程序上,可能会导致无法满足大区快内存需求,即使有足够但分散的小区块自由内存。使用自己定制的operator new和operator delete可以避免这样的问题。针对特定的需求,有时还可以提升性能。

3.为收集使用上的统计数据。在定制operator new和operator delete之前,应该首先了解软件如何使用动态内存。分配区块如何分布?寿命如何?它们是FIFO先进先出还是LIFO后进先出,或随机分配和归还?软件在不同执行阶段有不同的分配归还形态吗?任何时刻使用的最大动态分配量是多少?自己定义的operator new和operator delete可以轻松收集到这些信息。

作者的代码片实例:

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=malloc(realSize);
    if(!pMem) throw bad_alloc();

    *(static_cast<int*>(pMem))=signarure;
    *(reinterpret_cast<int*>(static_cast<Byte*>(pMem)+realSize-sizeof(int)))=signature;

    return static_cast<Byte*>(pMem)+sizeof(int);
}

上述代码没有考虑对齐问题

关于对齐:

C++要求所有operator new返回的指针都有适当的对齐(取决于数据类型)。malloc就是在这样的要求下工作。所以令operator new返回一个得自malloc的指针是安全的。但是上面实现中,我们偏移了一个int的大小,就不能保证其安全了

像对齐这类技术细节,可以区分内存管理器的质量。写一个能够运行的内存管理器并不难,难的是让它总是能够高效优良的运作。一般来说,若非必要,不要去写内存管理器。

作者接着阐述自定义new和delete的作用,如下:

为了增加分配和归还的速度。使用定制的针对特定类型对象的分配器,可以提高效率。例如,Boost提供的Pool程序库便是。如果在单线程程序中,你的编译器所带的内存管理具备线程安全,你可以写个不具备线程安全的分配器而大幅度改善速度。

为了降低缺省内存管理器带来的空间额外开销。泛用型分配器往往(虽然并非总是)不只比定制型慢,还使用更多空间,因为它们常常在每一个分配区块上招引某些额外开销。针对小型对象开放的分配器,例如Boost库的Pool,本质上消除了这样的额外开销。

为了弥补缺省分配器的非最佳对齐(suboptimal alignment)。X86体系结构上的double访问最快–如果它们是8-byte对齐。但是编译器自带的operator new并不保证分配double是8-byte对齐。

为了将相关对象成簇集中。如果特定的某个数据结构往往被一起使用,我们希望在处理这些数据时将“内存页错误”(page faults)的频率降至最低,那么为此数据结构创建另一个heap就有意义,这样就可以将它们成簇集中到尽可能少的内存也上。

为了获得非传统的行为。有时候我们需要做operator new和delete没做的事。例如,在归还内存时将其数据覆盖为0,以此增加应用程序的数据安全。

时间: 2024-08-26 03:32:40

Effective C++ 条款50的相关文章

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

1. 替换标准库提供的operator new或operator delete通常基于以下三个理由: 1). 用来检测运行上的错误.将"new 所得内存"delete掉却不幸失败会导致内存泄露,多次对同一块"new所得内存"施行delete会导致未定义行为,如果让operator new持有一串动态分配所得地址,而operator delete将地址从中移走,就可以很容易检测出上述错误;各式各样的变成错误会导致数据"overruns"(写入点在分

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

(一) 为什么有人想要替换operator new 和 operator delete呢?三个常见的理由: (1)用来检测运用上的错误. (2)为了强化效果. (3)为了收集使用上的统计数据. (二) 下面是个快速发展得出的初阶段global operator new,促进并协助检测"overruns"或"underruns". static const int signature = 0xDEADBEEF; typedef unsigned char Byte;

More Effective C++ 条款35 让自己习惯于标准C++ 语言

(由于本书出版于1996年,因此当时的新特性现在来说可能已经习以为常,但现在重新了解反而会起到了解C++变迁的作用) 1. 1990年后C++的重要改变 1). 增加了新的语言特性:RTTI,namespaces,bool,关键词mutable和explicit,enums作为重载函数之自变量所引发的类型晋升转换,以及"在class 定义区内直接为整数型(intergral) const static class members设定初值"的能力. 2). 扩充了Templates的特性

effective c++ 条款4 make sure that objects are initialized before they are used

1 c++ 类的数据成员的初始化发生在构造函数前 class InitialData { public: int data1; int data2; InitialData(int a, int b) { data1 = a: //this is assignment data2 = b; //this is assignment } /* InitialData(int a, int b):data1(a),data2(b) //this is initial {} */ } 2 不同cpp文

More Effective C++ 条款34 如何在一个程序中结合C++和C

1. C++和C混合使用的前提之一就是编译器产生兼容的目标文件(.lib和.dll等).所谓"兼容",指的是编译器在"预编译器相依的特性上"一致,如int和double大小,参数压栈机制等,只有在这个基础上才能讨论结合使用C++和C模块的问题. 2. 在1的基础上,要结合使用C++和C的模块,主要有以下几点需要注意: 1). name mangling(名称重整) Name mangling是C++用于支持函数重载的机制,它对重载的函数名称进行一定改变,使得每个函数

Effective C++ 条款3 尽可能用const

1. const可被施加于任何作用域内的对象,函数参数,函数返回类型,成员函数本体.用const修饰指针,如果const出现在*之前,表明指针不能更改所指向的对象的内容,如果const出现在*之后,表明指针只能指向同一块内存.另外int const*p和const int*p含义相同.如果对象成员有普通指针,那么构造该类的一个const对象时,const修饰使得该指针只能指向同一块内存,但指针指向的内容可以改变. 2. 将某些东西声明为const可以帮助编译器侦测出错误用法. 3. 编译器强制实

effective c++ 条款13 use object to manage resources.

请求的系统资源需要最终还回系统,为了避免遗忘返还这个动作,可以利用析构函数在object销毁时自动调用的特点来实现. 简单说就是用object来管理资源. 以内存资源为例 class Investment {}; Investment* creatInvestment(){...} // factory function to produce investment object void main() { Investment* pInv = creatInvestment();//call t

effective c++ 条款3 use const whereever you can

1 const 传达的意思应该是这个变量是常量不能更改 2 const 在 * 左边表示数据是const,在右边表示指针是const // char greeting[] = "hello"; char* p = greeting; //const *: const data //* const: const pointer char const *p1 = greeting; // const data char * const p2 = greeting; // const poi

effective c++ 条款18 make interface easy to use correctly and hard to use incorrectly

举一个容易犯错的例子 class Date { private: int month; int day; int year; public: Date(int month,int day,int year) { this->month = month; ... } } //wrong example Date date(30,3,1995);//should be 3,30 Date date(2,30,1995);//should be 3,30 使用类型可避免这个问题 class Month