天天看点

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

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

    Understand when it makes sense to replace new and delete.

    怎么会有人想要替换编译器提供的operator new或operator delete呢?我们可以列出如下三个常见的理由:

    ■ 用来检测运用上的错误.

    程序员从开始编写代码到调试直至最终完成,这一过程当中犯下各种各样的错误在所难免,这些错误就可能

导致内存泄露(memory leaks)、不确定行为产生、overruns(写入点在分配区块尾端之后)、underruns(写入点

在分配区块尾端之前)等不良结果的发生.如果我们自定义operator news,我们就可以超额分配内存,用额外空

间放置特定签名来检测类问题.

    ■ 为了强化效能.

    我们所用的编译器中自带的operator new和operator delete主要是用于一般的目的能够为各种类型的程序

所接受,而不考虑特定的程序类型.它们必须处理一系列需求,必须接纳各种分配形态,必须要考虑破碎问题等等

这些问题,因此编译器所带的operator new和operator delete采取中庸之道也是没办法的事情.它们的工作对每

个人都是适度地好,但不对特定任何人有最佳表现.通常可以发现,定制版之operator new和operator delete性

能胜过缺省版本.所谓的'胜过',就是它们比较快,有时甚至快很多,而且它们需要内存比较少,最高可省50%,所

以说对某些运用程序而言,将缺省new和delete替换为定制版本,是获得重大效能提升的办法之一.

    ■ 为了收集使用上的统计数据.

    收集你的软件如何使用其动态内存.分配区块的大小发布如何?寿命发布如何?它们倾向于以FIFO次序或LIFO

次序或随机次序来分配和归还?它们的运用形态是否随时间改变,也就是说你的软件在不同执行阶段有不同的分配

/归还形态吗?任何时刻所使用的最大动态内存分配量是多少?自行定义的operator new和operator delete使我们

得以轻松收集这些信息.

    基于上述三种理由,我不得不开始了写一个定制型operator new了,我的初稿看起来如下述代码所示,其中还存

在不少小错误,稍后我会完善它.

    typedef const int signature = 0xDEADBEEF;

    typedef unsigned char Byte;

    void* operator new( std::size_t size)throw(std::bad_alloc)

    {

        using namespace std;

        size_t real_size = size + 2 * sizeof(int);

        void* applied_memory = malloc( real_size );

        if( applied_memory == 0 ){

            throw bad_alloc();

        }

        //将signature写入内存的最前段落和最后段落.

        *(static_cast<int*>( applied_memory ) = signature;

        *(reinterpret_cast<int*>(static_cast<Byte*>( applied_memory )

        + real_size - sizeof(int) ) ) = signature;

        //返回指针,指向恰位于第一个signature之后的内存位置.

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

    }

    我刚才说了,这个版本有不少问题.而现在我只想专注一个比较微妙的主题:齐位(alignment).关于齐位的具

体是什么? 我假设大家都已经知道了,我在这里就不唠叨讲了.因为C++要求所有operator news返回的指针都有

适当的对齐(取决于数据类型).malloc就是在这样的要求下工作的,所以令operator new返回一个得自malloc的指

针是安全地.然而上述operator new中我并未返回一个得自malloc的指针,而是返回一个得自malloc且偏移一个

int大小的指针.没有人能够保证它的安全!我们可能因使用该版本的operator new导致获得一个未有适当齐位的

指针.那可能会造成程序崩溃或执行速度变慢.不论那种情况都不是我们希望看到的结果.

    写一个总是能够运行的内存管理器并不难,难的是它能够优良地运作.一般而言,本书的作者建议你在必要的

稍后才试着写写看.很多时候这是非必要的.某些编译器已经在它们的内存管理函数中切换至调试状态和志记状态

.快速浏览一下你的编译器文档,很可能就消除了你自行写new和delete的需要了.

    另一个选择是开放源码领域中的内存管理器.它们对许多平台都可用,你可以下载试试.Boost程序库的Pool就

是这样一个分配器,它对于常见的'分配大量小型对象'很有帮助.TR1支持各种类型特定对齐条件,很值得注意.

    讨论到这里,我们又可以为本款开头讨论的问题理由再添加几条了,呵呵:

    ■ 为了增加分配和归还的速度.

    泛用型分配器往往比定制型分配器慢,特别是当定制型分配器专门针对某特定类型之对象而设计时.

    ■ 为减低缺省内存管理器带来的空间额外开销.

    泛用型内存管理器往往还使用更多的内存,那是因为它们往往常常在每一个分配区块身上招引某些额外开销.

    ■ 为了弥补缺省分配器中的非最佳齐位.

    ■ 为了将相关对象成簇集中(详略).

    ■ 为了获得非传统行为(详略).

    OK,our topic talk is over!

    请记住:

    ■ 有许多理由需要写个自定义的new和delete,包括改善效能,对heap运用错误进行调试,收集heap使用信息.

继续阅读