多次调用同一对象的析构函数,其结果与标准中描述的不一样

Invoking the destructor of the same object multiple times, result is not the same as described in standard

本文关键字:结果 标准 不一样 描述 析构函数 调用 对象      更新时间:2023-10-16

我使用的是MS Visual Studio 2010。

我实现了一个双链表。

我想知道为什么在main函数中调用Clean方法后,调用对象的析构函数,在我引用对象后没有引发错误。

这里是我的一些双链表方法(相对于我的问题):

/*DoubleLinkedList.cpp */  
         DoubleLinkedList::~DoubleLinkedList(void)
        {
        cout << "Destructor invoked" << endl;
        // as for data nodes memory is allocated in heap we have to release it:
        const Node* const_iterator = m_head.m_next;
        while (const_iterator != &m_tail)
        {
            const_iterator = const_iterator->m_next;
            delete const_iterator->m_prev;
        }
    }
void DoubleLinkedList::Clean(void)
{
    cout << "Clean invoked" << endl;
    this->~DoubleLinkedList(); /* According to C++ 11 standart: Once a destructor is invoked for an object, the object no longer exists*/
}
/* main.cpp */
    int main(int argc, char* argv[])
    {
        DoubleLinkedList list;
        Circle c1, c2(MyPoint(1,1),50), c3(MyPoint(2,2),30);
        list.Front(&c1);
        list.Front(&c2);
        list.Front(&c3);
        list.Show();
        list.Sort();
        list.Show();
        list.Clean();
        list.Show(); /* Recall how Clean method is implemented. As list no longer exist, run-time error is expected here, but flow of executon continues and Show, Push_back preforms fine*/
        list.Push_back(&c1);
        list.Push_back(&c2);
        list.Push_back(&c3);

问题: *如c++ 11标准中所述,在调用析构函数后-对象不再存在*,为什么在调用析构函数后我仍然能够使用对象?

这里需要考虑的重要事项是对象的生存期。对象的生存期必须在为该对象分配存储的时间内存在。在分配存储的时间内,多个对象可以一个接一个地存在于一个存储位置。

通常,当对象被销毁时(通过超出作用域或调用delete),对象的生命周期结束,然后它的内存被释放。然而,当显式调用析构函数时,所做的只是结束对象的生命周期。该标准没有对象"不存在"的定义,但它有其生命周期结束的概念。

类型为T的对象的生存期结束于:

  • 如果T是具有非平凡析构函数(12.4)的类类型,则开始析构函数调用,或
  • 对象占用的存储空间被重用或释放。

现在,在对象生命周期结束并且在其存储被释放之前的状态下,只有非常特定的事情可以做。标准定义了指针和左值在这种状态下可以做的事情。在您的示例中,list是一个全局值,因此我们将查看其规则:

[…在对象生命周期结束后和对象存储之前被占用的被重用或释放,任何指向原始对象的glvalue都可以被使用,但只能以有限的方式使用。[…如果:

程序有未定义的行为
  • 左值到右值的转换(4.1)应用于这样的glvalue,
  • glvalue用于访问非静态数据成员或调用对象的非静态成员函数,或者
  • glvalue被隐式转换(4.10)为对基类类型的引用,或者
  • glvalue用作static_cast(5.2.9)的操作数,除非最终转换为cv char&或cv unsigned char&,或
  • glvalue用作dynamic_cast(5.2.7)的操作数或typeid的操作数。

第二个列表项在这里适用。你有未定义的行为,因为你在对象生命周期结束后访问非静态成员函数。

在对象被销毁(即调用析构函数)后使用它是UB。UB表示未定义行为。所以…