考虑以下代码片段就地构建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类型。我知道,如果涉及多个继承和类似的东西,事情就会变得不可能。
答案 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 new
和operator delete
这些保留的展示位置。void* operator new(std::size_t size, void* ptr) noexcept;
2返回:
ptr
。
3备注:故意不执行任何其他操作。
这保证了allocate-function返回传递的指针不变。