为什么我们说对象超出范围时调用析构函数?

Why We Say Destructor Call When Object Goes out of Scope?

我了解到当对象超出范围时会调用析构函数,并且析构函数还会删除对象。好的,但是这里发生了什么?

我是显式调用析构函数,如果它删除了对象那么为什么要隐式调用析构函数?即使现在没有对象,因为它已经被显式析构函数调用删除了。对不起,我可能是明确和隐含地错了但是试着理解我的问题。

#include <iostream>
using namespace std;
class A{
    public:
        A(){
            cout << "Constructor" << endl;
        }
        ~A(){
            cout << "Destructor" << endl;
        }

};
int main(){
    A obj;
    obj.~A();
    cout << "End" << endl;
}

现在

obj.~A();

上面一行删除了对象。那为什么又要调用析构函数呢?即使那里没有对象。

输出:

Constructor
Destructor
End
Destructor

超出范围的对象会调用其析构函数。这是 C++ 语言标准强制要求的不可更改的保证。您是否事先手动调用了析构函数并不重要;当它超出范围时,将调用析构函数。如果您编写的析构函数被调用两次就会发生坏事,或者如果编译器插入析构函数代码的额外行为不想被调用两次,那么坏事就会发生。

这是您永远不应手动调用析构函数的众多原因之一。

您的对象不包含任何指针。它在调用时不会释放析构函数中的任何对象,因此在多次调用时不会造成任何伤害。 如果析构函数删除指针并且那些指针没有设置为0,这在析构函数中很常见,那么第二次调用析构函数将是有害的,因为它会导致内存错误。

析构函数不是删除对象,而是析构它。 并且除非对象是 POD 类型(基本上,你 可以在 C 中定义),销毁一个已经销毁的对象是 未定义的行为。由于所有变量都会自动具有 他们的析构函数在他们的生命周期结束时调用,你应该 永远不要明确地调用它们的析构函数。同样,如果你 使用 new 的非放置形式创建了对象: 显式调用析构函数不会释放内存;你 应该使用 delete,它调用析构函数然后释放 记忆。

你唯一应该显式调用析构函数的时间是 您已使用 placement new 来构造对象,以便 单独分配和初始化。今天,这样的技术 真的应该算是高级了,难得一见 程序员需要它们。

您提供的 class 不管理任何数据,因此其析构函数没有任何对象可释放。这就是为什么您可以两次调用析构函数而不会产生任何后果的原因。这是一个无法通过相同测试的示例 class:

#include <iostream>


class A {
 public:
  A() {
    std::cout << "constructing..." << '\n';
    val = new int;
  }

  ~A() {
    std::cout << "destructing..." << '\n';
    delete val;
  }

 private:
  int* val;
};


int main() {
  A obj;
  obj.~A();
  std::cout << "End" << '\n';
  return 0;
}

构造函数在堆上为 val 分配 space,析构函数运行 delete 以释放 space。 ~A()main() 中被调用了两次(首先是显式调用,然后是隐式调用),这可以理解地导致内存错误。

查看此 link 了解有关析构函数如何工作的更多信息: https://isocpp.org/wiki/faq/dtors