首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在一个对象被销毁后,标量类型的子对象会发生什么情况?

在一个对象被销毁后,标量类型的子对象会发生什么情况?
EN

Stack Overflow用户
提问于 2012-07-24 19:04:48
回答 2查看 454关注 0票数 3

考虑以下代码(对于renewcleanse的不同值):

代码语言:javascript
复制
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参数!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-07-24 19:24:11

我最初有这两个引号,但现在我认为它们实际上只是指定了像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)的存储时间。

因此,在本例中,不必担心编译器“释放”exit之前的存储。

3.8p2中的一个非规范性说明提到,"12.6.2描述基和成员子对象的生存期“,但那里的语言只讨论初始化和析构函数,而不是”存储“或”生存期“,因此我得出结论,对于琐碎类型的子对象,”生存期“的定义不受影响。

如果我正确地解释了所有这些,当renew为false时,完整类对象的生存期将在显式析构函数调用结束时结束,但是int子对象的生存期将一直持续到程序结束。

3.8第5和第6段指出,指针和对“分配的存储”的引用在任何对象的生命周期之前或之后都可以以有限的方式使用,并列出了许多您可能无法使用它们的事情。与ref == 42所需的表达式一样,从Lvalue到rvalue的转换就是其中之一,但如果int的生存期尚未结束,这不是一个问题。

因此,我认为使用renew假,程序是良好的格式和assert成功!

使用renew true,存储被程序“重用”,因此原始int的生存期已经结束,另一个int的生存期开始了。但接下来是3.8段第7段:

如果在对象的生存期结束后,在对象占用的存储被重用或释放之前,在原始对象占用的存储位置创建一个新对象,指向原始对象的指针,引用原始对象的引用,或原始对象的名称,则可以自动引用新对象,并且一旦新对象的生存期启动,就可以使用它来操作新对象,如果:

  • 新对象的存储完全覆盖原始对象占用的存储位置,以及
  • 新对象与原始对象的类型相同(忽略顶级cv-限定符),以及
  • 原始对象的类型不是const限定的,如果是类类型,则不包含任何类型为const限定或引用类型的非静态数据成员,以及
  • 原始对象是T类型的最派生对象(1.8),而新对象是T类型的最派生对象(也就是说,它们不是基类子对象)。

这里的第一个重点是最棘手的问题。对于像您的T这样的标准布局类,相同的成员肯定总是在同一个存储中。我不确定当类型不是标准布局时,这在技术上是否是必需的。

尽管是否仍然可以使用ref,但在这个示例中还有另一个问题。

12.6.2第8段:

X的构造函数调用完成后,如果X成员在构造函数主体的复合语句执行期间既未初始化也未给值,则该成员具有不确定的值。

这意味着,如果将t.mem设置为零或设置为0xDEADBEEF,则实现是兼容的(有时调试模式在调用构造函数之前实际上会执行此类操作)。

票数 3
EN

Stack Overflow用户

发布于 2012-07-24 19:08:33

您没有销毁内存,您只手动调用析构函数(在此上下文中,它与调用普通方法没有什么不同)。t变量的内存(堆栈部分)没有“释放”。因此,此断言将始终与当前代码一起传递。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11637611

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档