【问题标题】:What's the purpose of having a separate "operator new[]"?拥有一个单独的“operator new[]”的目的是什么?
【发布时间】:2010-03-23 12:43:03
【问题描述】:

看起来operator newoperator new[] 具有完全相同的签名:

void* operator new( size_t size );
void* operator new[]( size_t size );

并做同样的事情:要么返回一个指向足够大的原始(未以任何方式初始化)内存块的指针,要么抛出异常。

当我使用newoperator new[] 创建对象时,在内部调用operator new - 当我使用new[] 创建对象数组时。上述两个特殊函数在 C++ 内部以完全相同的方式调用,我看不出这两个调用有什么不同的含义。

让两个不同的函数具有完全相同的签名和完全相同的行为的目的是什么?

【问题讨论】:

    标签: c++ memory-management


    【解决方案1】:

    操作符可以被覆盖(对于特定的类,或在命名空间内,或全局),如果您想以不同于数组分配的方式处理对象分配,这允许您提供单独的版本。例如,您可能希望从不同的内存池进行分配。

    【讨论】:

      【解决方案2】:

      我对此有相当好的了解,坦率地说,从界面的角度来看没有任何理由。

      我能想到的唯一可能的原因是允许对实现进行优化提示,operator new[] 可能会被调用以分配更大的内存块;但这是一个非常非常脆弱的假设,您可以 new 一个非常大的结构或 new char[2] 这并不算大。

      请注意,operator new[] 不会为数组计数或其他任何东西添加任何神奇的额外存储空间。 new[] 运算符的工作是计算需要多少开销(如果有)并将正确的字节数传递给 operator new[]

      [使用 gcc 的测试表明 new[] 不需要额外的存储空间,除非正在构造的数组成员的类型具有非平凡的析构函数。]

      从接口和合约的角度来看(除了需要使用正确的相应释放函数)operator newoperator new[] 是相同的。

      【讨论】:

      • 只有在 delete[] 操作符必须在循环中调用 dtors 时才需要存储。 (您几乎说过,但对于任何阅读此内容并想知道的人来说都是明确的。)
      • @Roger Pate:是的,谢谢。我擅长“几乎”说话。当然,它实际上如何知道需要调用多少个析构函数严格来说是一个实现细节,但将其与动态分配的数组一起存储是最明显的实现。
      【解决方案3】:

      在 C++ 的设计和演变(第 10.3 节)中,Stroustrup 提到如果对象 X 的 new 运算符本身用于分配对象 X 的数组,那么 X::operator new() 的编写者将不得不处理也有数组分配,这不是 new() 的常见用法并增加了复杂性。因此,不考虑使用 new() 进行数组分配。然后,没有简单的方法为动态数组分配不同的存储区域。解决方案是为数组提供单独的分配器和释放器方法:new[] 和 delete[]。

      【讨论】:

      • 这才是真正的答案。其他人正在猜测为什么会这样,因为我们确实将它们分开了。
      【解决方案4】:

      一个目的是它们可以由用户单独定义。因此,如果我想将单个堆分配对象中的内存初始化为 0xFEFEFEFE,将堆分配数组中的内存初始化为 0xEFEFEFEF,因为我认为这将有助于我进行调试,那么我可以。

      这是否值得是另一回事。我想如果您的特定程序主要使用非常小的对象和非常大的数组,那么您可以分配不同的堆,希望这会减少碎片。但同样你可以识别你分配大数组的类,并为这些类覆盖operator new[]。或者operator new 可以根据大小在不同的堆之间切换。

      要求的措辞实际上有所不同。一个为指定大小的任何对象分配对齐的内存,另一个为指定大小的任何数组分配对齐的内存。我认为没有任何区别——大小为 1 的数组肯定与对象具有相同的对齐方式——但我可能弄错了。默认情况下,数组版本返回与对象版本相同的事实强烈表明没有区别。或者至少对象的对齐要求比数组的对齐要求更严格,我无法理解......

      【讨论】:

      • ...尤其是作为数组类型的对象仍然是一个对象。
      • 是的,我只是想编辑添加它。但是数组类型的对象不是 any 对象,它是所有对象的严格子集的一部分,因此可能具有较弱的对齐要求。我不确定sizeof (T[1]) > sizeof (T) 是否合法,但如果是,那么您可以想象在某些实现中,大小为 8 字节的 array 的对齐要求可能只有 4(因为那是T 可以是最大的),而大小为 8 字节的 object 的对齐要求可能是 8(因为存在 8 对齐类型)。
      • 我很确定 sizeof(T[1]) > sizeof(T) 是不允许的,因为我不记得了;也许与指针算术的定义有关。不过,T[1] 的对齐方式可能是 > sizeof T[1]。我认为将 placement-new 和数组放入operator new 分配的内存中是合法的,因为数组是一个对象。
      • 哦,是的,如果sizeof(T[1]) != sizeof(T) ,整个数组大小计算sizeof array / sizeof array[0] 将是完全错误的,而且我相当确定常见的数组大小计算是明确定义的。
      • 好点。至于新的放置,我认为我从未将它与数组一起使用过(或者就这一点而言,在“真实代码”中)。如何删除使用placement new 创建的数组?一定有人记住了大小,所以要么是应用程序,要么你不能将MyClass[2] 放置到2*sizeof(MyClass) 字节的存储空间中。
      【解决方案5】:

      标准表示new T 调用operator new( )new T[ ] 导致调用operator new[]( )。如果你愿意,你可以超载它们。我相信默认情况下它们之间没有区别。标准说它们是可替换的 (3.7.3/2):

      该库为全局分配和释放函数提供默认定义。一些全球性的 分配和解除分配功能是可替换的(18.4.1)。一个 C++ 程序最多只能提供一个 可替换分配或释放函数的定义。任何此类函数定义都会替换 库中提供的默认版本 (17.4.3.4)。以下分配和释放函数 (18.4) 在程序的每个翻译单元的全局范围内隐式声明

      void* operator new(std::size_t) throw(std::bad_alloc);  
      void* operator new[](std::size_t) throw(std::bad_alloc);  
      void operator delete(void*) throw();  
      void operator delete[](void*) throw();  
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-03-25
        • 1970-01-01
        • 1970-01-01
        • 2021-10-04
        • 1970-01-01
        • 1970-01-01
        • 2020-10-11
        • 1970-01-01
        相关资源
        最近更新 更多