其他分享
首页 > 其他分享> > c – 对象被销毁后,标量类型的子对象会发生什么?

c – 对象被销毁后,标量类型的子对象会发生什么?

作者:互联网

请考虑此代码(针对续订和清理的不同值):

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);
}

断言是否保证通过?

我知道不推荐这种风格.像“这不是一种合理的做法”这样的观点在这里并不重要.

我想要一个答案,显示标准报价的完整逻辑证明.编译器编写者的观点也可能很有趣.

编辑:现在有两个问题合二为一!请参阅续订参数(续订== 0,这是原始问题).

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

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

解决方法:

我首先得到这两个引号,但现在我认为它们实际上只是指定像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 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类型的成员子对象.

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).

因此,不必担心编译器在此示例中退出之前“释放”存储.

3.8p2中的非规范性注释提到“12.6.2描述了基础和成员子对象的生命周期”,但那里的语言只讨论初始化和析构函数,而不是“存储”或“生命周期”,所以我得出结论,不影响普通类型子对象的“生命周期”定义.

如果我正在解释所有这一切,当update为false时,完整类对象的生命周期在显式析构函数调用结束时结束,但int子对象的生命周期一直持续到程序结束.

3.8第5和第6段说,在任何对象的生命周期之前或之后对“已分配存储”的指针和引用可以以有限的方式使用,并列出许多你可能不用它们做的事情. Lvalue-to-rvalue转换,就像表达式ref == 42所要求的那样,是其中之一,但如果int的生命周期尚未结束,那么这不是问题.

所以我认为更新错误,程序结构良好,断言成功!

如果更新为true,则程序会“重用”存储,因此原始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 type T (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 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,则实现是顺从的(有时调试模式实际上会在调用构造函数之前执行此类操作).

标签:object-lifetime,c,language-lawyer,destructor,explicit-destructor-call
来源: https://codeday.me/bug/20191003/1849595.html