考虑这段代码(renew
和 cleanse
的不同值):
struct T {
int mem;
T() { }
~T() { mem = 42; }
};
// identity functions,
// but breaks any connexion between input and output
int &cleanse_ref(int &r) {
int *volatile pv = &r; // could also use cin/cout here
return *pv;
}
void foo () {
T t;
int &ref = t.mem;
int &ref2 = cleanse ? cleanse_ref(ref) : ref;
t.~T();
if (renew)
new (&t) T;
assert(ref2 == 42);
exit(0);
}
assert
是否保证通过?
据我所知,不推荐这种风格。 意见,例如“这不是一个合理的做法”,在这里不感兴趣。
我想要一个显示来自标准引用的完整逻辑证明 的答案。编译器编写者的意见也可能很有趣。
编辑:现在两个问题合二为一!请参阅 renew
参数(renew == 0
,这是原始问题)。
编辑 2:我想我的问题确实是:什么是成员对象?
编辑 3:现在使用另一个 cleanse
参数!
最佳答案
我最初有这两个引号,但现在我认为它们实际上只是指定像 int &ref = t.mem;
这样的事情必须在 t
的生命周期内发生。在您的示例中,它的作用。
12.7 第 1 段:
For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.
第 3 段:
To form a pointer to (or access the value of) a direct non-static member of an object
obj
, the construction ofobj
shall have started and its destruction shall not have completed, otherwise the computation of the pointer value (or accessing the member value) results in undefined behavior.
这里有一个T
类型的完整对象和一个int
类型的成员子对象。
3.8 第 1 段:
The lifetime of an object of type
T
begins when:
- storage with the proper alignment and size for type
T
is obtained, and- if the object has non-trivial initialization, its initialization is complete.
The lifetime of an object of type
T
ends when:
- if
T
is a class type with a non-trivial destructor (12.4), the destructor call starts, or- the storage which the object occupies is reused or released.
顺便说一句,3.7.3 p1:
The storage for these [automatic storage duration] entities lasts until the block in which they are created exits.
和 3.7.5:
The storage duration of member subobjects, base class subobjects and array elements is that of their complete object (1.8).
因此,在此示例中,不用担心编译器会在 exit
之前“释放”存储。
3.8p2中的一个非规范性注释中提到“12.6.2描述了基类和成员子对象的生命周期”,但是那里的语言只谈初始化和析构函数,而不谈“存储”或“生命周期”,所以我得出结论该部分不影响普通类型子对象的“生命周期”定义。
如果我对这一切的解释是正确的,当 renew
为 false 时,完整类对象的生命周期在显式析构函数调用结束时结束,但 int 的生命周期
子对象继续到程序结束。
3.8 第 5 段和第 6 段说,在任何对象的生命周期之前或之后对“分配的存储”的指针和引用可以以有限的方式使用,并列出了很多你不能用它们做的事情。左值到右值的转换,如表达式 ref == 42
所要求的,是其中之一,但如果 int
的生命周期尚未到来,这不是问题结束了。
所以我认为 renew
为 false,程序是良构的并且 assert
成功了!
当renew
为真时,存储被程序“重用”,所以原来的int
的生命周期结束了,另一个int的生命周期结束了
开始。但随后我们进入 3.8 第 7 段:
If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:
- the storage for the new object exactly overlays the storage location which the original object occupied, and
- the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and
- the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and
- the original object was a most derived object (1.8) of type
T
and the new object is a most derived object of typeT
(that is, they are not base class subobjects).
这里的第一个要点是最棘手的。对于像您的 T
这样的标准布局类,同一个成员当然必须始终位于同一个存储中。当类型不是标准布局时,我不确定这在技术上是否需要。
尽管 ref
是否仍然可以使用,但这个例子中还有另一个问题。
12.6.2 第 8 段:
After the call to a constructor for class
X
has completed, if a member ofX
is neither initialized nor given a value during execution of the compound-statement of the body of the constructor, the member has indeterminate value.
意味着如果将 t.mem
设置为零或 0xDEADBEEF
则实现是兼容的(有时 Debug模式实际上会在调用构造函数之前执行此类操作)。
关于c++ - 一个对象被销毁后,标量类型的子对象会发生什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11637611/