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

时间:2014-10-28 17:23:02

标签: 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 );
}

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

请注意,我在这里专门询问POD类型。我知道,如果涉及多个继承和类似的东西,事情就会变得不可能。

3 个答案:

答案 0 :(得分:12)

此断言将始终成立,因为new需要返回具有MAXIMUM可能对齐的内存块。顺便说一句 - 你的第一个assert()毫无价值,因为正常情况new没有返回nullptr - 它会抛出或中止,只有&#34; nothrow new&#34;可以返回nullptr

答案 1 :(得分:6)

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

注意:这是基于C ++ 11中 new-expression 的规范。看起来C ++ 14会改变措辞,所以未来答案可能会有所不同。

答案 2 :(得分: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)。当它这样做时,存储由实现提供,或者通过扩展另一个 new-expression 的分配来提供。 [...]
  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;
     

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

这保证了allocate-function返回传递的指针不变。