替换子对象的存储期限

时间:2019-02-17 15:39:45

标签: c++ language-lawyer placement-new subobject storage-duration

关于下面的代码

int main()
{
    struct S { int i; } s { 42 };
    new (&s.i) int { 43 };
}

[basic.stc]/2

  

动态存储持续时间与new表达式创建的对象相关联。

There is similar question,同意新放置的位置创建具有动态存储持续时间的对象。因为没有其他措辞可以适用于该问题中的示例。

这是一个精心设计的示例,其中有措辞说出一些有趣的东西。 [basic.stc.inherit]/1说:

  

子对象和引用成员的存储期限是其完整对象的期限

[intro.object]/2保证创建的int对象是s的子对象:

  

如果在与成员子对象或数组元素e(可能在其生命周期内或可能不在其生命周期内)关联的存储器中创建对象,则在以下情况下,创建的对象是e的包含对象的子对象:
  (要求已得到满足,我不会在此处复制它们

那么,新创建的int对象具有哪个存储期限?是动态还是自动?

1 个答案:

答案 0 :(得分:1)

这个问题很有趣。的确,有关动态存储持续时间和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),则每个编译器都会产生相同的结果。

实际上,动态创建对象(放置新对象)并不一定意味着对象生命周期与创建对象的范围无关。但是,您需要确保在适当的时候销毁对象,因此将其视为动态存储持续时间。

这里的关键是分配。只有内存分配才能使对象持续时间真正独立于创建对象的范围。这是在标准中表达的,但可能不尽如人意。让我们从 basic.stc/2 中的完整子句开始:

  

静态,线程和自动存储持续时间与   对象由声明引入,由对象隐式创建   实施。动态存储持续时间与   由new表达式创建的对象。

我会在这里理解,只有当对象没有被第一句覆盖时,最后一句才适用。但这是目前的个人解释。因此,唯一可以确定的是,如果出现重叠,则需要格外小心。

因此,让我们仔细看看动态存储持续时间[basic.stc.dynamic] / 1

  

可以在程序执行期间动态创建对象(...)。 C ++实现提供对以下内容的访问和管理:   通过全局分配功能运算符new和   运算符new []和全局释放函数运算符delete   和运算符delete []。 [注意:   21.6.2.3不执行分配或取消分配。 —尾注]

第二句话清楚地表明动态存储意味着分配。然后是有趣的注释,它恰好指的是 [new.delete.placement] / 1 一章:

  

这些功能是保留的; C ++程序可能未定义函数   用于替换C ++标准库中的版本。   6.7.4的规定不适用于以下保留的表格   运算符new和运算符delete。

6.7.4部分是 basic.stc.dynamic 部分。这意味着用于新放置的特殊分配不会创建动态存储。

动态存储和动态存储持续时间不是一个事实,并且同一件事使整个内容难以表达:

  • 动态存储期限意味着您必须注意对象的生命周期并在必要时删除
  • 动态存储意味着对存储持续时间没有限制。
  • 在动态存储(尤其是在自动存储位置)之外的其他位置创建动态存储持续时间对象需要格外小心,因为您需要确保在可用存储时销毁该对象。如果只是在新放置中用相同类型的对象替换对象,则将受益于离开范围时将销毁该对象的代码。但是在任何其他情况下,您都需要保重。

这里有一个 online demo ,用于新放置和销毁,并查看当封闭对象超出范围时会发生什么。它使用Tracer类,将比int突出显示不同的情况(包括在调用新的展示位置之前删除先前的对象)。

结论:我认为,在具有如此悠久的历史和如此众多的贡献者的任何标准中,都无法避免某些歧义和循环性。但是在这种情况下,您可以看到问题本身具有您最初期望的更多方面。