在C++中放置新的和对齐

ant*_*red 12 c++ c++11

考虑以下代码片段就地构建POD(普通旧数据)结构的实例:

#include <new>
#include <cassert>
#include <cstddef>

struct Test
{
    int a;
    char b;
    double c;
};

int main()
{
    const std::size_t minimumNumberOfBytes = sizeof( Test ) * 4;

    // Get a block of memory that can accommodate a Test instance and then some!
    void* const ptrToMemBlock = new char[ minimumNumberOfBytes ];
    assert( ptrToMemBlock );

    // Construct a Test instance in-place.
    const Test* const testInstance( ::new ( ptrToMemBlock ) Test() );

    // Is this assumption guaranteed to be true?
    assert( testInstance == ptrToMemBlock );
}
Run Code Online (Sandbox Code Playgroud)

最终断言()所代表的假设是否保证始终是正确的?或者可以想象编译器可能决定构建Test实例,比如在我在placement-new调用中指定的内存块开始之后的几个字节?

请注意,我在这里具体询问POD类型.我知道,如果涉及多个继承和类似的东西,事情就会变得越来越多.

Fre*_*pin 12

此断言将始终保持,因为new需要以MAXIMUM可能的对齐方式返回内存块.BTW - 你的第一个assert()没有价值,正常new情况下不会返回nullptr- 它会抛出或中止,只有"nothrow new"才能返回nullptr.

  • 实际上,答案可能也是错误的.最大对齐保证仅适用于`new char [N]`,不适用于一般类型. (4认同)
  • @MooingDuck:关键是`new T [N]`的结果可能不会与`U`对齐,因为`new char [N]`总是适当对齐.参看 [expr.new]/11. (3认同)

Mik*_*our 6

是的,断言将成立.new创建单个对象的任何表达式都必须sizeof(Test)从分配函数中请求确切的存储字节; 所以它必须将对象放在该存储的开头,以便有足够的空间.

注意:这是基于C++ 11中新表达式的规范.看起来C++ 14会改变措辞,所以未来答案可能会有所不同.

  • @antred:不,没有什么可以阻止过度分配.但是当一个`new`表达式调用`operator new`来分配内存时,它必须准确地请求它所需的数量; 所以它必须假设它只不过是那个. (6认同)
  • @antred:新的可能过度分配,但没有办法告诉,所以你必须总是假设它给了你确切的金额. (3认同)
  • @Deduplicator:好的,看起来答案在C++ 14中会更复杂.我会提到我指的是C++ 11,它没有提到类似的东西. (2认同)

Ded*_*tor 6

是的,最后一个assert保证保持,因为这种形式的placement-new必须始终返回传递的指针,而不是为自己使用任何空间:

5.3.4新的 [expr.new]

8 new-expression可以通过调用分配函数来获取对象的存储空间(3.7.4.1).[...]
10允许实现省略对可替换全局分配函数的调用(18.6.1.1,18.6.1.2).当它这样做时,存储由实现提供,或者通过扩展另一个新表达式的分配来提供.[...]
11当new-expression调用分配函数并且该分配尚未扩展时,newexpression将请求的空间量作为类型的第一个参数传递给分配函数std::size_t.该参数不得小于正在创建的对象的大小; 仅当对象是数组时,它可能大于正在创建的对象的大小.
[...]

你的new-expression调用全局placement-new分配函数.
这是不可替换的功能,因此不能扩展或省略分配.
此外,您不是分配数组而是分配单个对象,因此根本不会发生请求填充.

18.6.1.3安置表格 [new.delete.placement]

1这些函数是保留的,C++程序可能无法定义用于替换标准C++库(17.6.4)中的版本的函数.的(3.7.4)的规定并不适用于这些保留的安置形式operator newoperator delete.

void* operator new(std::size_t size, void* ptr) noexcept;
Run Code Online (Sandbox Code Playgroud)

2返回:ptr.
3备注:故意不执行任何其他操作.

这保证了allocation-function返回传递的指针不变.