为什么 ::运算符 new[] 是必需的,而 ::运算符 new 就足够了

Why is ::operator new[] necessary when ::operator new is enough?

本文关键字:new 运算符 为什么      更新时间:2023-10-16
众所周知

,C++标准定义了两种形式的全局分配函数:

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

而且,C++标准草案(18.6.1.2 n3797(说:

227( 这不是经营者的直接责任 新建或运算符删除以记录重复 数组的计数或元素大小。执行这些操作 数组中的其他位置新建和删除表达式。阵列新 但是,表达式可能会将大小参数增加到运算符 新增获取空间以存储补充信息。

让我困惑的是:

如果我们从标准中删除void* operator new[](size_t);,而只使用void* operator new(size_t)呢?定义冗余全局分配函数的基本原理是什么?

我认为::operator new[]对于相当专业的系统可能很有用,在这些系统中,"大但少"数组可能由与"小但多"对象不同的分配器分配。然而,它目前是一种遗物。

operator new可以合理地期望对象将在返回的确切地址构造,但operator new[]不能。分配块的第一个字节可能用于大小"cookie",数组可能稀疏初始化等。这种区别对于成员operator new变得更有意义,而成员可能是针对其特定类的。

无论如何,::operator new[]不可能是必需的,因为std::vector(通过std::allocator(,这是目前获取动态数组的最流行方法,忽略了它。

在现代C++中,自定义分配器通常是比定制operator new更好的选择。实际上,应该完全避免使用new表达式,而使用容器(或智能指针等(类,这些类提供了更多的异常安全性。

::operator new[] 和 ~ delete[]便于内存使用调试,是审计分配和释放操作的中心点;然后,您可以确保数组形式用于两者或两者都不使用。

如果非常不寻常/粗糙的调音用途,也有很多合理的:

  • 从单独的池中分配数组,也许是因为这极大地提高了动态分配的小型单个对象的平均缓存命中率,

  • 数组/非数组数据的不同内存访问提示 (ALA madvise (

所有这些都有点奇怪,超出了99.999%程序员的日常关注范围,但为什么要阻止它成为可能呢?

标准 (n3936( 清楚地表明这两个运算符用于不同但相关的目的。

operator new调用函数void* operator new(std::size_t) 。第一个参数必须与运算符的参数相同。它返回一个适当对齐的存储块,该存储块可能比所需大一些。

operator new[]调用函数void* operator new[](std::size_t)。第一个参数可能大于提供给运算符的参数,以便在数组索引需要时提供额外的存储空间。两者的默认实现是简单地调用 malloc((。

operator new[]的目的是支持专门的阵列索引(如果可用(。它与内存池或其他任何东西无关。在使用此功能的符合实现中,该实现将在额外空间中设置专用表,编译器将生成代码以用于指令或调用使用这些表的库库支持例程。C++使用数组的代码而无法使用 new[] 在这些平台上会失败。

我个人并不了解任何这样的实现,但它类似于支持某些大型机(CDC,IBM等(所需的功能,这些大型机的架构与我们熟悉和喜爱的英特尔或RISC芯片完全不同。

在我看来,接受的答案是不正确的。


为了完整起见,标准(n3936 主要在 S5.3.4 中(包含以下内容。

    分配"数组
  1. 对象"或"非数组对象"之间的区别
  2. 对"阵列分配开销"的引用,暗示可能需要额外的存储,并且可能(以某种方式(用于重复计数或元素大小。

没有提到内存池,也没有任何提示表明这可能是一个考虑因素。

我确信有适当的用例需要单独的new[]new,但我还没有遇到过这种分离唯一可能的用例,仅此而已。

但是,我是这样看的:由于用户调用不同版本的运算符new,如果C++标准只定义了一个operator new并且同时newnew[],那么他们就会犯肆意和故意丢失信息的罪。这里有(字面意思(有一点信息,可能对某人有用,我认为委员会的人不会凭良心把它扔掉!

此外,必须实现额外的new[]对我们其他人来说是一个非常非常小的不便,如果有的话,所以保留一点信息的权衡胜过必须在一小部分程序中实现一个简单的功能。

C++编程语言:特别版第 423 页 说

_The operator new()operator delete()功能允许用户接管单个对象的分配和取消分配; operator new[]()operator delete[]() 在数组的分配和解除分配方面具有完全相同的作用。

感谢Tony D纠正了我对这种细微差别的误解。

哇,我很少发现我非常确定C++的东西 - 我一定在 Objective-C 上花了太多时间!

原始错误答案

这很简单 - new[] 表单在经典 C 数组的每个元素上调用构造函数。

因此,它首先为所有对象分配空间,然后迭代调用每个槽的构造函数。