为什么要替换默认的新运算符和删除运算符?

为什么人会替换默认的运营商newdelete一个自定义newdelete运营商?

这是在极其明亮的C ++ FAQ中重载新的和删除的继续:
运算符超载。

此常见问题解答的后续条目是:
我应该如何编写符合ISO C ++标准的自定义newdelete操作符?

注意:答案是基于Scott Meyers的More Effective C ++的经验教训。
(注意:这是一个Stack Overflow的C ++常见问题解答的入口,如果你想批评在这个表单中提供常见问题的想法,那么开始所有这些的meta上的贴子将成为这样做的地方。那个问题在C ++聊天室中进行监控,常见问题的想法首先出现在C ++聊天室中,所以你的答案很可能会被那些提出这个想法的人阅读。)



首先,真的有很多不同的newdelete操作符(真的是一个任意数字)。

首先,有::operator new::operator new[]::operator delete::operator delete[] 。 其次,对于任何类X ,都有X::operator newX::operator new[]X::operator deleteX::operator delete[]

在这些之间,比全局操作符重载特定于类的操作符更为常见 - 特定类的内存使用遵循足够特定的模式,您可以编写提供对默认值进行实质性改进的操作符,这是相当常见的。 在全球范围内精确或专门预测内存使用情况通常要困难得多。

可能还值得一提的是,虽然operator newoperator new[]是相互独立的(对于任何X::operator newX::operator new[] )也是如此,两者的要求没有区别。 一个将被调用来分配一个对象,另一个分配一个对象数组,但是每个对象仍然只是接收需要的大量内存,并且需要返回一块内存块的地址(至少)。

说到需求,回顾其他需求可能是值得的:1:全球运营商必须是真正的全球运营商 - 您不可以在名称空间内放置一个名称空间,也不要在特定翻译单位中放置一个名称空间。 换句话说,只有两个级别可以发生重载:特定于类的重载或全局重载。 不允许在诸如“命名空间X中的所有类”或“翻译单元Y中的所有分配”之间的中间点。 具体类的经营者必须是static -但你实际上并不需要声明为静态的-它们是静态的你是否明确声明他们static或没有。 正式的,全球运营商很多人都将内存对齐,以便它可以用于任何类型的对象。 非正式地说,在一个方面有一点摆动空间:如果你得到一个小块的请求(例如,2个字节),你只需要为一个尺寸最大的对象提供内存对齐,因为试图存储更大的东西无论如何都会导致未定义的行为。

在介绍了这些预备知识之后,让我们回到最初的问题,即为什么您想要让这些运营商超载。 首先,我应该指出,全球运营商超载的原因往往与特定类别运营商超载的原因大不相同。

由于它更常见,我会首先讨论特定类的操作符。 特定于类的内存管理的主要原因是性能。 这通常以两种形式(或两种形式)出现:提高速度或减少碎片。 速度由于内存管理器仅处理特定大小的块而得到改善,所以它可以返回任何空闲块的地址,而不是花时间检查块是否足够大,如果块是分块(大部分)都会以相同的方式减少碎片 - 例如,预先分配足够大的N个对象的块会提供N个对象所需的空间; 分配一个对象的内存值将精确地分配一个对象的空间,而不是多个单独的字节。

全局内存管理操作员负担过重的原因有很多种。 其中许多是面向调试或检测的,例如跟踪应用程序所需的全部内存(例如,准备移植到嵌入式系统),或者通过显示分配和释放内存之间的不匹配来调试内存问题。 另一种常见策略是在每个请求块的边界之前和之后分配额外的内存,并在这些区域中写入独特的模式。 在执行结束时(也可能在其他时间),检查这些区域以查看代码是否已写入分配的边界之外。 另一种方法是尝试通过自动化至少某些内存分配或删除方面来提高易用性,例如使用自动垃圾回收器。

非默认的全局分配器也可以用来提高性能。 一个典型的例子就是替换一个通常很慢的默认分配器(例如,至少某些4.x版本的MS VC ++版本会为每个分配/删除操作调用系统HeapAllocHeapFree函数)。 我在实践中看到的另一种可能性是在使用SSE操作时发生在Intel处理器上。 这些操作在128位数据上。 虽然这些操作不管对齐如何都能正常工作,但是当数据与128位边界对齐时,速度会得到改善。 一些编译器(例如MS VC ++再次2)不一定强制对齐到更大的边界,因此即使使用默认分配器的代码可以工作,替换分配也可以为这些操作提供显着的速度改进。


  • C ++标准的§3.7.3和§18.4(或C ++ 0x中的§3.7.4和§18.6,至少在N3291中)涵盖了大部分要求。
  • 我感到有必要指出,我不打算选择微软的编译器 - 我怀疑它有这么多问题,但我碰巧使用了很多,所以我倾向于意识到它的问题。

  • 许多计算机体系结构要求将特定类型的数据放置在特定类型的地址的内存中。 例如,体系结构可能要求指针出现在4的倍数(即,四字节对齐)或双倍必须出现在8的倍数(即,八字节对齐)的地址处。 如果不遵循这些约束,可能会导致运行时出现硬件异常。 其他体系结构更为宽容,并可能允许它通过降低性能来工作。

    为了阐明:如果一个架构要求例如double数据是八字节对齐的,那么没有什么可以优化的。 保证适当大小的任何类型的动态分配(例如malloc(size)operator new(size)operator new[](size)new char[size] ,其中size >= sizeof(double) ) 。 如果一个实现没有做出这个保证,那么它不符合。 在这种情况下,改变operator new以做“正确的事情”将会是“修复”实施的尝试,而不是优化。

    另一方面,一些体系结构允许对一种或多种数据类型进行不同(或全部)对齐,但根据这些相同类型的对齐情况提供不同的性能保证。 然后,一个实现可能会返回内存(同样,假设请求的大小合适),这个内存是次优对齐的,并且仍然符合要求。 这就是这个例子。

    链接地址: http://www.djcxy.com/p/2005.html

    上一篇: Why would one replace default new and delete operators?

    下一篇: What are rvalues, lvalues, xvalues, glvalues, and prvalues?