一个对象被销毁后,标量类型的子对象会发生什么? [英] After an object is destroyed, what happens to subobjects of scalar type?

查看:202
本文介绍了一个对象被销毁后,标量类型的子对象会发生什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑此代码(对于 renew cleanse 的不同值):

Consider this code (for different values of renew and 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 保证通过?

我知道这种风格是不是推荐的。

I understand that this style is not recommended. Opinions like "this is not a sound practice" are not of interest here.

我想要一个答案。 来自标准报价的完整逻辑证明。编译器作者的意见可能也很有趣。

I want an answer showing a complete logical proof from standard quotes. The opinion of compiler writers might also be interesting.

编辑:现在有两个问题!参见 renew 参数( renew == 0 ,这是原始问题)。

now with two questions in one! See the renew parameter (with renew == 0, this is the original question).

编辑2:我想我的问题是:什么是成员对象?

EDIT 2: I guess my question really is: what is a member object?

编辑3:现在有另一个 cleanse 参数!

EDIT 3: now with another cleanse parameter!

推荐答案

我首先有这两个引号,只需指定 int& ref = t.mem; 必须在 t 的生命周期内发生。

I first had these two quotes, but now I think they actually just specify that things like int &ref = t.mem; must happen during the lifetime of t. Which it does, in your example.

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段:


形成指向(或访问值)的直接非静态成员的指针一个对象 obj obj 的构造应该已经启动,并且其销毁将不会完成,否则计算指针值(或访问成员值)会导致未定义的行为。

To form a pointer to (or access the value of) a direct non-static member of an object obj, the construction of obj 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 的成员子对象。

We have here a complete object of type T and a member subobject of type int.

3.8第1段: / p>

3.8 paragraph 1:


T 类型的对象的生命周期开始于:

The lifetime of an object of type T begins when:


  • 存储,并且

  • 如果对象具有非平凡的初始化,则其初始化完成。

code> T 结束于:

The lifetime of an object of type T ends when:


  • if T 是具有非平凡析构函数(12.4)的类类型,析构函数调用开始,或

  • 对象占用的存储被重用或释放。

  • 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:

By the way, 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).

因此,不必担心编译器会在退出之前释放存储

So no worries about the compiler "releasing" the storage before the exit in this example.

3.8p2中的非规范性注释提到12.6.2描述了基本和成员子对象的生命周期关于初始化和析构函数,而不是存储或生存期,因此我认为这一节不会影响对子类型的子对象的生命周期的定义。

A non-normative note in 3.8p2 mentions that "12.6.2 describes the lifetime of base and member subobjects," but the language there only talks about initialization and destructors, not "storage" or "lifetime", so I conclude that section does not affect the definition of "lifetime" for subobjects of trivial type.

我正在解释这一切,当 renew 为false时,完整的类对象的生命周期在显式析构函数调用结束时结束,但是 c $ c> int 子对象继续到程序的结尾。

If I'm interpreting all this right, when renew is false, the lifetime of the complete class object ends at the end of the explicit destructor call, BUT the lifetime of the int subobject continues to the end of the program.

3.8第5和6段说指针和引用 在任何对象的生命周期之前或之后可以有限的方式使用,并列出了很多你可能不会与他们做的事情。 Lvalue-to-rvalue转换,像表达式 ref == 42 需要,是这些事情之一,但是如果 int 尚未结束。

3.8 paragraphs 5 and 6 say that pointers and references to "allocated storage" before or after any object's lifetime can be used in limited ways, and list a whole lot of things you may not do with them. Lvalue-to-rvalue conversion, like the expression ref == 42 requires, is one of those things, but that's not an issue if the lifetime of the int has not yet ended.

因此,我认为 renew false,程序格式良好, assert 成功!

So I think with renew false, the program is well-formed and the assert succeeds!

使用续订 true,存储被程序重用,所以原来的 int 的生命周期结束,另一个生命周期 int 开始。但是,我们进入3.8第7节:

With renew true, the storage is "reused" by the program, so the lifetime of the original int is over, and the lifetime of another int begins. But then we get into 3.8 paragraph 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:


  • 新对象完全覆盖原始对象占用的存储位置,并且

  • 新对象与原始对象的类型相同(忽略顶级cv限定符) / li>
  • 原始对象的类型不是const限定的,如果类类型不包含类型为const限定的任何非静态数据成员或引用类型,

  • 原始对象是类型 T 的最大派生对象(1.8),而新对象是类型 T (也就是说,它们不是基类子对象)。

  • 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 type T (that is, they are not base class subobjects).


b $ b

这里的第一个问题是最棘手的问题。对于像 T 这样的标准布局类,同一个成员必须总是在同一个存储中。

The first bullet point here is the trickiest one. For a standard-layout class like your T, the same member certainly must always be in the same storage. I'm not certain whether or not this is technically required when the type is not standard-layout.

虽然 ref

Although whether ref may still be used or not, there's another issue in this example.

12.6.2第8段:

12.6.2 paragraph 8:


调用构造函数如果 X 的成员既未被初始化也未被赋予值,则 X

After the call to a constructor for class X has completed, if a member of X 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 (有时调试模式实际上会在调用构造函数之前做这些事情)。

Meaning the implementation is compliant if it sets t.mem to zero or 0xDEADBEEF (and sometimes debug modes will actually do such things before calling a constructor).

这篇关于一个对象被销毁后,标量类型的子对象会发生什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆