新的运算符分配函数顺序连续性和初始值

new operator allocation function order contiguity & initial values

C++ 标准第 3.7.3.1 节说

"The order, contiguity, and initial value of storage allocated by successive calls to an allocation function is unspecified."

顺序和连续性是什么意思?为什么它是未指定的?为什么初始值也未指定?

它本质上意味着 new 运算符可以从系统中它认为必要的任何位置分配内存,而不依赖于程序的任何分配顺序。

订单
意味着分配器不受限于 return 稳定增加或减少(或任何其他模式)的地址。这是有道理的,因为在程序的生命周期中,内存通常会被多次回收和重用。可以从分配器请求存储顺序以某种方式定义,但在代码执行和内存效率方面,与相当大的开销相比,收益很小(如果有的话)。

也就是说,如果B在A之后分配,C在B之后分配,那么A、B、C在内存中可能以任意顺序出现(ABC、BAC、CAB、CBA、...) .您知道 A、B 和 C 的每个地址都是有效的,但仅此而已。

正如@Deduplicator 所指出的,并发编程中存在一个众所周知的问题"the ABA problem"。间接地,这是新分配的对象原则上可以具有 any 地址这一事实的结果(请注意,这有点高级)。
当您使用比较交换指令以原子方式修改内存位置时,例如在无锁列表或队列中,会出现 ABA 问题。您的 假设 是比较交换检测到其他人何时同时修改了您正在尝试修改的指针。然而,真相只是检测地址的位模式是否不同。现在,您可能会释放一个对象并将另一个对象(向分配器请求存储)推入容器,而分配器会返回完全相同的地址——这是绝对合法的。另一个线程使用 compare-exchange 指令,它顺利通过。砰!
为了防止这种情况,无锁结构通常使用带有内置参考号的指针。

相邻
这是一个类似的约束,它基本上表示在后续分配中的分配器 return 之间可能存在也可能不存在 "holes"。

这意味着,如果您分配一个大小为 8 的对象,然后分配另一个对象,分配器可能 return 地址 A 和地址 A+8。但它也可以 return 解决 A+10 而不是第二次分配 自行决定 .
这是几乎每次分配都会经常发生的事情,因为除了实际对象之外,分配器通常还存储元数据,并且通常根据 "buckets" 将内存组织到特定大小(通常为 16 字节)。
所以如果你分配一个整数(通常是 4 个字节)或一个指针(通常是 4 或 8 个字节),那么在这个和你分配的下一个东西之间会有一个 "hole"。

同样,可能 要求分配器 returns 以连续的方式对象,但这将意味着严重的性能影响相对便宜。

初值
这意味着您必须正确初始化您的对象并且不能假定它们具有任何特定值。不,内存将不会自动归零[1].

要求分配器对内存进行零初始化是可能的,但效率会比可能的低(尽管不像其他两个约束那样具有破坏性)。


[1] 好吧,在某些情况下,它会。在第一次将新页面提供给进程之前,操作系统通常会将所有页面清零。这样做是为了安全,所以没有 secret/confidential 数据被泄露。但是,这是一个超出 C++ 标准范围的实现细节。