对象被销毁后,标量类型的子对象会发生什么
After an object is destroyed, what happens to subobjects of scalar type?
考虑以下代码(对于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段:
对于具有非平凡析构函数的对象,在析构函数完成执行后引用该对象的任何非静态成员或基类会导致未定义的行为。
和第3段:
要形成指向对象
obj
的直接非静态成员的指针(或访问其值),obj
的构建应已开始,其销毁应尚未完成,否则指针值的计算(或访问成员值)将导致未定义的行为。
这里有一个类型为T
的完整对象和一个类型int
的成员子对象。
3.8第1段:
类型为
对于类型T
的对象的生存期从以下时间开始:T
,获得了具有适当排列和大小的
- 存储,并且
- 如果对象进行了非平凡的初始化,那么它的初始化就完成了
类型为
T
的对象的生存期在以下情况下结束:
- 如果
T
是一个具有非平凡析构函数(12.4)的类类型,则析构函数调用启动,或者- 对象占用的存储器被重新使用或释放
顺便说一句,3.7.3 p1:
这些[自动存储持续时间]实体的存储将持续到创建它们的块退出为止。
和3.7.5:
成员子对象、基类子对象和数组元素的存储时间是其完整对象的存储时间(1.8)
所以不用担心编译器"释放";在本例中为CCD_ 17之前的存储器。
3.8p2中的非规范性注释提到;12.6.2描述了基本子对象和成员子对象的寿命;但那里的语言只谈论初始化和析构函数,而不是";存储";或";寿命";,因此我得出结论,该部分不影响";寿命;对于琐碎类型的子对象。
如果我正确地解释了这一切,当renew
为false时,完整类对象的生存期将在显式析构函数调用结束时结束,但int
子对象的生命期将持续到程序结束。
3.8第5和第6段指出;分配的存储器";在任何对象的生命周期之前或之后,都可以以有限的方式使用,并列出许多您可能无法使用它们的事情。像表达式ref == 42
所要求的那样,从左值到右值的转换就是其中之一,但如果int
的生命周期尚未结束,这就不是问题。
所以我认为renew
为假,程序形成良好,assert
成功了!
在renew
为真的情况下;"重复使用";则原int
的寿命结束,另一个int
的寿命开始。但接下来我们进入3.8第7段:
如果在对象的生存期结束后,在对象所占用的存储被重用或释放之前,在原始对象所占据的存储位置创建了一个新对象,则指向原始对象的指针、引用原始对象的引用或原始对象的名称将自动引用新对象,一旦新对象的生存期开始,就可以用来操作新对象,如果:
- 新对象的存储正好覆盖原始对象占用的存储位置,并且
- 新对象与原始对象的类型相同(忽略顶级cv限定符),并且
- 原始对象的类型不是const限定的,如果是类类型,则不包含任何类型是const限定或引用类型的非静态数据成员,并且
- 原始对象是类型为
T
的最派生对象(1.8)并且新对象是类型T
的最派生的对象(即它们不是基类子对象)
这里的第一个要点是最棘手的。对于像T
这样的标准布局类,同一个成员肯定总是在同一个存储中。当类型不是标准布局时,我不确定这在技术上是否是必需的。
尽管ref
是否仍然可以使用,但在这个例子中还有另一个问题。
12.6.2第8段:
对类
X
的构造函数的调用完成后,如果在执行构造函数主体的复合语句期间,X
的成员既没有初始化也没有给定值,则该成员的值不确定。
如果它将t.mem
设置为零或0xDEADBEEF
,则意味着实现是兼容的(有时调试模式会在调用构造函数之前实际执行这些操作)。
您没有销毁内存,只是手动调用了析构函数(在这种情况下,它与调用普通方法没有什么不同)。t
变量的内存(堆栈部分)未"释放"。因此,这个断言将始终与当前代码一起传递。
- 什么时候调用组成单元对象的析构函数
- 返回常量对象引用 (getter) 和仅返回字符串有什么区别?
- int数据类型的指针指向的是什么,如果是一个类的私有数据成员,我们创建了该类的两个对象?
- 是什么让放置新调用对象的构造函数?
- 当 std::move 与 C 样式数组或不移动对象时会发生什么
- 在什么情况下,两个堆栈分配的结构对象的 this 点指向同一个地址?
- 这个C++编译器优化(在自身的实例上调用对象自己的构造函数)的名称是什么,它是如何工作的?
- 什么更好?返回对象指针列表?或返回指向对象列表的指针?
- 在什么条件下使用 std::memcpy 在对象之间复制是安全的?
- 处理影响跨不同线程共享对象的定时回调的最佳方法是什么?
- 具有相同特征的两个对象是否只在内存中存储一次?无论定义它们的函数是什么,都是不同的
- 为对象分配整数.输出将是什么?
- 当使用对象名称后带有 [] 的类对象时,您究竟会传入什么.C++
- 什么是"undetectable means",它们如何更改 C/C++ 程序的对象?
- 在C++中,创建'n'数量的对象的推荐方法是什么,其中n是用户定义的。我该怎么做?
- 使用 gtest 框架在单元测试代码中检查目标对象的私有变量的最佳实践是什么?
- 删除对象(具有不同类型)的引用时会发生什么情况?
- 一个对象什么时候可以有非零大小的一个,但不能同时具有一个或多个字节的存储
- 在C++中,对象什么时候真正被销毁?delete(ptr)做什么
- C++在文件中保存类对象和从文件中读取类对象.什么是最好的选择