【问题标题】:Accessing an object after calling the destructor调用析构函数后访问对象
【发布时间】:2019-09-03 14:40:41
【问题描述】:

在下面的代码中,我明确地调用了~destructor()。但是,该对象仍然是可访问的。我怎样才能删除它(让它消失)?

class Queue {
    public:
    node* top = NULL;//points to the top of the queue 
    //methods:
    void enqueue(int data);//adds a node to the queue
    void dequeue();
    //printing 
    void print();
    //destructor 
    ~Queue();
};

还有析构函数:

Queue::~Queue() {
    //The destructor deletes all items from HEAP
    //Then sets the top to 0
    while (top != NULL)
        this->dequeue();//dequeue until there are NO more items
    top = 0;
}

在 Source.cpp 中:

Queue q;

q.enqueue(1);
q.enqueue(2);
q.enqueue(3);
q.enqueue(4);

q.dequeue();
q.dequeue();
q.print();

q.~Queue();
q.print();//Here I need to have an ERROR!

q.enqueue(7);//Here I need to have an ERROR!
q.print();//Here I need to have an ERROR!

输出是:

4 3 7

我预计会出错:

标识符“q”未定义

【问题讨论】:

  • 很少有充分的理由显式调用析构函数。你所做的只是未定义的行为,你不能指望这样的错误信息。
  • 调用析构函数不会删除标识符。访问已销毁的对象会调用 未定义的行为
  • q 仍然存在。你所做的只是调用它的析构函数。即使q 不在那里,您仍然可以访问它。调用未定义行为的结果是未定义的。
  • 现实是相反的:你不应该在对象被销毁后访问它
  • 这是XY issue?。您的目标是删除所有项目吗?如果是这样,那么您应该为此目的拥有一个 clear() 成员函数,而不是尝试错误地调用析构函数来执行此操作。

标签: c++ oop destructor


【解决方案1】:

从字面上看//Here I need to have an ERROR!,你会这样做:

{
    Queue q;

    q.enqueue(1);
    q.enqueue(2);

    q.dequeue();
    q.print();
}
q.print();        // THIS WILL PRODUCE AN ERROR

您似乎对堆栈分配对象的生命周期有误解。堆栈对象超出范围时会自动销毁。在示例中,q 的范围以} 结尾。

自己调用析构函数几乎总是错误的(我遇到过一种可以显式调用析构函数的情况)。为什么?考虑一下:

{
    Queue q;
    q.~Queue();   // DONT DO THIS !
}

您调用了析构函数,但是当它超出范围时,它会再次被销毁,并且您会遇到严重的运行时错误。

你在代码中做了什么:

Queue q;
q.~Queue();
q.print();

undefined behaviour

还要注意,调用析构函数并不是删除对象时发生的全部事情。当堆栈分配的对象被删除时,首先调用它的析构函数,然后释放分配的内存。通常你不想干扰这个过程,幸运的是你很少需要这样做。

我怎样才能删除它(让它消失)?

你不能让它“消失”。当一个对象被销毁时,内存中的位和字节不会被擦除。那将是非常低效的。实际上,我认为 C 的 free 有一个更好且更容易混淆的名称。内存被释放以供以后使用,它不会被清除,以至于无法读取之前的内容。

为了进一步阅读,我建议您参考这个对一个略有不同但相关的问题的详尽答案:Can a local variable's memory be accessed outside its scope?

另外,我建议您阅读RAII,它依赖于自动调用析构函数。

TL;DR:如果你想清除Queue然后写:

q.clear();
q.print(); // prints an empty queue!

永远不要调用堆栈分配对象的析构函数!它会被自动调用。

【讨论】:

  • 值得补充一点,在对象上调用析构函数不会释放该对象的存储空间。这只是一个功能。在正常使用情况下(对象为deleted 或超出范围)在析构函数运行以释放存储空间后会出现其他情况。
  • @user4581301 确实如此。添加了一个注释,但我的知识并没有超出“其他东西出现并做某事”,所以我尽量保持模糊;)如果你认为你可以改进,请随时编辑
  • 我在同一条船上。我已经看到它是如何在某些运行时实现的,但还没有深入研究标准的那部分以了解什么是必需的,什么不是必需的。对于我所做的那种工作来说,离人迹罕至的地方太远了。
猜你喜欢
  • 2010-11-10
  • 1970-01-01
  • 2011-05-24
  • 1970-01-01
  • 2016-01-29
  • 1970-01-01
  • 2019-07-04
  • 2015-05-02
  • 2023-03-24
相关资源
最近更新 更多