user227660 Asked:2020-11-14 00:09:11 +0000 UTC2020-11-14 00:09:11 +0000 UTC 2020-11-14 00:09:11 +0000 UTC 析构函数如何工作 772 解释当调用一个对象的析构函数时究竟是什么释放了内存,因为默认情况下它有一个空的主体。 c++ 2 个回答 Voted Best Answer AnT stands with Russia 2020-11-14T00:47:29Z2020-11-14T00:47:29Z 形式上,析构函数与释放“它的”对象的内存无关。内存的释放由外部代码完成,在释放前调用析构函数。所以从这个角度来看你的问题有点没有意义。 析构函数是一个特殊的函数。就算是一具空虚的身体,也不代表它“什么都不做”。例如,析构函数总是隐式调用给定类的子对象的析构函数,即使它具有明显的“空”主体。 很明显,析构函数可能负责显式释放对象直接拥有的无关资源(包括内存)。但在这种情况下,析构函数的主体也不会为空。 在“传统”实现中,奇怪的是,当析构函数是虚拟的时,对象的动态内存释放函数通常是从析构函数内部隐式调用的(而不是通过外部代码)。为了确保operator delete类重载的正确行为(如果有的话),这样的技巧是必要的。但这些是内部实现细节。 第三段提到了以下情况。 在 C++ 语言中,运算符的抽象算法delete被简化为两个步骤的序列: 调用正确的对象析构函数 调用正确的函数来释放“原始”内存operator delete(void *)。 如您所知,该功能operator delete(void *)可以由用户替换/重载。允许在特定类中覆盖全局::operator delete(void *)函数和重载静态函数。operator delete(void *)同时,语言规范要求选择一个特定的对象,operator delete(void *)就好像它的名称查找是从被删除对象的析构函数中完成的一样。 例如 #include <iostream> struct B { virtual ~B() { std::cout << "B::~B" << std::endl; } void operator delete(void *p) { std::cout << "B::operator delete" << std::endl; ::operator delete(p); } }; struct D : B { virtual ~D() { std::cout << "D::~D" << std::endl; } void operator delete(void *p) { std::cout << "D::operator delete" << std::endl; ::operator delete(p); } }; int main() { B* pb = new B; B* pd = new D; delete pb; delete pd; } 在这样的代码中,在执行delete pb析构函数之后执行时B::~B,必须调用B::operator delete,在执行delete pd析构函数之后执行时D::~D,必须D::operator delete调用。换句话说,即使函数operator delete始终是类的静态成员,它实际上应该表现得像虚(!) 函数。 为了满足这种语言要求,大多数实现只是将正确的调用移到operator delete析构函数中。operator delete因此,以破坏析构函数的虚拟性为代价,免费实现了函数所需的“虚拟性” 。 同时,很明显operator delete(void *)只需要调用动态内存中分配的完整对象,而对于其他对象则没有必要(即不可能)。考虑到这一点,编译器为析构函数提供一个隐式布尔参数,告诉析构函数是否调用operator delete. 所以在上面的例子中,析构函数实际上看起来像这样: B::~B(bool call_delete) // неявный параметр { std::cout << "B::~B" << std::endl; if (call_delete) // неявно B::operator delete(this); // неявно } ~D::D(bool call_delete) // неявный параметр { std::cout << "D::~D" << std::endl; B::~B(false); // неявно if (call_delete) // неявно D::operator delete(this); // неявно } 在这种情况下,表达式delete pbanddelete pd会简单地变成虚拟调用pb->~B(true)and pd->~B(true)。第一个在B::~B,第二个在D::~D。 顺便说一下,GCC 编译器在早期版本中完全按照上述方式实现了这种方法——通过一个隐藏的布尔参数,而在现代版本中,它通过生成两个单独的析构函数实现了相同的方法。 gbg 2020-11-14T00:13:32Z2020-11-14T00:13:32Z 语言标准允许在析构函数的主体(即使它是空的)之后,内存将被释放。自动的。
形式上,析构函数与释放“它的”对象的内存无关。内存的释放由外部代码完成,在释放前调用析构函数。所以从这个角度来看你的问题有点没有意义。
析构函数是一个特殊的函数。就算是一具空虚的身体,也不代表它“什么都不做”。例如,析构函数总是隐式调用给定类的子对象的析构函数,即使它具有明显的“空”主体。
很明显,析构函数可能负责显式释放对象直接拥有的无关资源(包括内存)。但在这种情况下,析构函数的主体也不会为空。
在“传统”实现中,奇怪的是,当析构函数是虚拟的时,对象的动态内存释放函数通常是从析构函数内部隐式调用的(而不是通过外部代码)。为了确保
operator delete类重载的正确行为(如果有的话),这样的技巧是必要的。但这些是内部实现细节。第三段提到了以下情况。
在 C++ 语言中,运算符的抽象算法
delete被简化为两个步骤的序列:operator delete(void *)。如您所知,该功能
operator delete(void *)可以由用户替换/重载。允许在特定类中覆盖全局::operator delete(void *)函数和重载静态函数。operator delete(void *)同时,语言规范要求选择一个特定的对象,operator delete(void *)就好像它的名称查找是从被删除对象的析构函数中完成的一样。例如
在这样的代码中,在执行
delete pb析构函数之后执行时B::~B,必须调用B::operator delete,在执行delete pd析构函数之后执行时D::~D,必须D::operator delete调用。换句话说,即使函数operator delete始终是类的静态成员,它实际上应该表现得像虚(!) 函数。为了满足这种语言要求,大多数实现只是将正确的调用移到
operator delete析构函数中。operator delete因此,以破坏析构函数的虚拟性为代价,免费实现了函数所需的“虚拟性” 。同时,很明显
operator delete(void *)只需要调用动态内存中分配的完整对象,而对于其他对象则没有必要(即不可能)。考虑到这一点,编译器为析构函数提供一个隐式布尔参数,告诉析构函数是否调用operator delete. 所以在上面的例子中,析构函数实际上看起来像这样:在这种情况下,表达式
delete pbanddelete pd会简单地变成虚拟调用pb->~B(true)andpd->~B(true)。第一个在B::~B,第二个在D::~D。顺便说一下,GCC 编译器在早期版本中完全按照上述方式实现了这种方法——通过一个隐藏的布尔参数,而在现代版本中,它通过生成两个单独的析构函数实现了相同的方法。
语言标准允许在析构函数的主体(即使它是空的)之后,内存将被释放。自动的。