这个问题很有趣。确实,关于动态存储持续时间和new 表达式的措辞并不排除placement-new。
公式中的歧义是由于需要涵盖的案例种类繁多:
int main()
{
struct Simple { int i; } s { 42 };
new (&s.i) int { 43 }; // the lifecyle of the newly created object
// will auto, since Simple will be destroyed when
// going out of scope
struct Complex { char s[256]; } c;
Simple *p = new (&c.s) Simple; // the lifecycle of the newly created object
// is dynamic. You'll need to delete it in time.
// because compiler doesn't know about its true nature
// and memory will be lost when going out of scope
}
幸运的是,该标准足够精确,因此如果您提供正确的代码(即没有 UB),每个编译器都会产生相同的结果。
事实上,对象的动态创建(放置new)并不一定意味着对象的生命周期独立于对象被创建的范围。但是您需要确保对象会在适当的时候被销毁,因此它被认为是动态存储持续时间。
这里的关键是分配。只有内存分配才能使对象持续时间真正独立于创建它的范围。这在标准中有所表达,但可能不如它本来的那么清楚。让我们从 basic.stc/2 中的完整子句开始:
静态、线程和自动存储持续时间与
由声明引入并由
实施。动态存储持续时间与
由 new 表达式创建的对象。
我在这里理解的是,最后一句仅适用于对象尚未被第一句涵盖的情况。但这是目前个人的解释。所以唯一可以确定的是,如果出现重叠,需要格外小心。
让我们更仔细地看一下动态存储持续时间[basic.stc.dynamic]/1
可以在程序执行期间动态创建对象 (...)。 C++ 实现提供访问和管理,
通过全局allocation函数操作符new和动态存储
operator new[] 和全局释放函数 operator delete
和运算符删除[]。 [ 注意:非分配形式描述在
21.6.2.3 不执行分配或解除分配。 — 尾注 ]
第二句清楚地表明动态存储意味着分配。然后是有趣的注释,它正是指[new.delete.placement]/1一章:
这些功能是保留的; C++ 程序不能定义函数
取代了 C++ 标准库中的版本。 该
6.7.4 的规定不适用于这些保留的安置形式
运算符 new 和运算符 delete。
第 6.7.4 节是 basic.stc.dynamic 节。这意味着用于放置新的特殊分配不会创建动态存储。
动态存储和动态存储持续时间不是一回事,这使得整个内容难以表达:
- 动态存储持续时间意味着您必须注意对象生命周期并在必要时删除
- 动态存储意味着对存储持续时间没有限制。
- 在动态存储之外的其他位置(尤其是在自动存储位置)创建动态存储持续时间对象需要格外小心,因为您需要确保在存储可用时将其销毁。如果您只是在placement-new 中用相同类型的对象替换一个对象,您将受益于在离开作用域时会破坏该对象的代码。但在任何其他情况下,您都需要小心。
这里有一个 online demo 来使用placement-new 和destroy,看看当封闭对象超出范围时会发生什么。它使用Tracer 类,比int 更好地突出不同的情况(包括在调用新放置之前删除前一个对象)。
结论:我认为在任何具有如此悠久历史和众多贡献者的标准中都无法避免一些模糊性和循环性。但是在这种情况下,您可以看到问题本身具有您最初预期的更多方面。