【发布时间】:2021-11-08 21:00:35
【问题描述】:
根据[basic.life/1](我的粗体强调):
T 类型对象的生命周期开始于:
- 存储具有适合类型 T 的正确对齐和大小,并且
- 它的初始化(如果有)已完成(包括空初始化)([dcl.init]), 除了如果对象是联合成员或其子对象,它的生命周期仅在联合成员是联合中的初始化成员([dcl.init.aggr]、[class.base.init])或如中所述时才开始[class.union] 和 [class.copy.ctor],以及 [allocator.members] 中描述的除外。
类型 T 的对象 o 的生命周期在以下时间结束:
- 如果 T 是非类类型,则对象被销毁,或者
- 如果 T 是类类型,析构函数调用开始,或者
- 对象占用的存储空间被释放,或被未嵌套在 o ([intro.object]) 中的对象重用。
所以我们有:
|------------|----|--------------|----------|-------------|----|--------------|
<----------> <------------> <--------> <-----------> <------------>
storage object object object storage
allocation construction lifetime destruction deallocation
<----------------------------------------------->
storage duration
根据[basic.life/8](我的粗体强调):
如果在对象的生命周期结束之后,在对象占用的存储空间被重用或释放之前,在原对象占用的存储位置创建一个新的对象,指针指向原始对象的引用、指向原始对象的引用或原始对象的名称将自动引用新对象,并且一旦新对象的生命周期开始,就可以用来操作新对象,如果原始对象可以被新对象透明地替换(见下文)。
想到两个问题:
- 放置
new运算符是重用存储的唯一方法吗? - [basic.life/8] 中指定的结果是否真的需要销毁原始对象(即条件“在对象的生命周期结束后”)?
【问题讨论】:
-
2.可以,但不应该,因为这个要求使联合在 C++ 中不可用(如果联合成员最初不是活动的,则不可能使其活动,因为创建成员类型的对象不会使成员的名称引用到新的[子]对象)
-
@LanguageLawyer 你的意思是在this answer的代码中的成员赋值运算符中需要声明
name.~basic_string();吗? -
@Maggyero:这是必需的,因为您需要销毁该对象,因为它是一个重要的析构函数,您的代码依赖于调用它来释放内存。
-
不,对象的生命周期仅因存储重用而结束。这对于 [basic.life]/8 来说已经足够了。 Mebby 我的第一条评论令人困惑。不需要显式的析构函数调用,但也不应该是生命周期结束。
-
@LanguageLawyer 关于 2,好的,所以我们同意应该从标准中删除“对象生命周期结束后”的条件,因为原始对象的生命周期无论如何都会因存储重用而结束。
标签: c++ language-lawyer storage object-lifetime placement-new