假设sizeof(T) <= sizeof(void*)
,以下定义良好/可移植? ...
void* storage = 0;
new (&storage) T(t);
这似乎可以用作合理的小对象优化,如果是的话。
答案 0 :(得分:3)
您正在传递一个有效的地址,如果指向的内存大到足以包含该对象,并且它的对齐方式与该对象兼容,那么它就是完美的代码。
如果sizeof(T) <= sizeof(storage)
,对齐应该是隐式正确的。但是你可能会对它产生偏执并明确地设置它:
alignas(T) void* storage = 0;
虽然我认为实际上并不需要手动设置对齐,sizeof(T) <= sizeof(storage)
意味着保证正确对齐,但我并不是百分之百确定。
请注意,仅因为storage
的类型为void*
并不意味着什么。此特定展示位置由标准定义为:
void* operator new(std::size_t count, void* ptr);
地址参数是void*
,意味着指向的类型可以是任何类型。唯一的要求是它是有效内存的地址。
但是,如果storage
超出范围,如果包含在其中的对象需要被破坏,则会被破坏。您需要在storage
超出范围之前调用析构函数。
另请注意,“放置删除”(如果需要,会自动调用,无法编写执行此操作的代码)将永远不会释放内存。因此,即使调用了放置删除(例如构造函数抛出时),堆栈中的storage
仍然正常。
答案 1 :(得分:0)
假设您已检查对齐方式是否正确,如Nikos C。答案中所述:
new
是合法的,但名称storage
可能不再用于以任何方式引用该对象(除非T
= void *
)。你需要写:
T *ptr = new(&storage) T(t);
然后使用ptr
。
见[basic.life] / 7:
如果在对象的生命周期结束之后并且在重用或释放对象占用的存储之前,则在原始对象占用的存储位置创建新对象,指向原始对象的指针,引用原始对象的引用,或原始对象的名称将自动引用新对象,并且一旦新对象的生命周期开始,可用于操作新对象,如果:
- 新对象的存储空间正好覆盖原始对象占用的存储位置和
- 新对象与原始对象的类型相同(忽略顶级cv限定符),和 [...]
第二个项目符号点为false,因此原始对象的名称不能用于操纵新对象。
注意:您当然不能做cout << storage;
之类的事情。但我不清楚写作是否合法:
new(&storage) T(t);
T *ptr = reinterpret_cast<T>(&storage);
即。如果原始对象的名称仅用于获取其地址,然后该地址用于操纵新对象,那么这是否算作通过原始对象的名称操纵新对象?在任何一种情况下,都可以通过使用帖子顶部建议的代码来避免该问题。