2

我想知道默认类析构函数在被调用时是否真的做任何事情。

我一直在研究它,我发现如果我用一个调用它自己的析构函数的函数创建一个类,它根本不会做任何事情(即所有变量保持不变,实例仍然存在并且可用)。

这是否意味着可以将类析构函数视为所有类都具有的继承虚函数,并且可以重新定义它(删除指针等并清除成员变量)但是如果不重新定义它会做什么都没有?

如果是这样,析构函数不能本质上用作“清除所有数据”类型的函数,并通过清除动态内存分配的变量并重新使用它而不是让计算机找到新的变量来提高代码的某些部分的效率吗?堆上的内存块?

谢谢。

4

4 回答 4

2

我一直在研究它,我发现如果我用一个调用它自己的析构函数的函数创建一个类,它根本不会做任何事情(即所有变量保持不变,实例仍然存在并且可用)。

考虑这段代码:

#include <iostream>

struct A
{
    ~A()
    {
        std::cout << "A::~A" << std::endl;
    }
};

struct B
{
    A a;
};

int main()
{
    B b;
    std::cout << "Calling b.~B()" << std::endl;
    b.~B();
    std::cout << "Done" << std::endl;
}

你会看到调用B的默认析构函数调用了A析构函数,因为B它包含一个A

Calling b.~B()
A::~A
Done
A::~A

只有当b超出范围时,堆栈才会展开并B::~B()调用综合,反过来,A::~A()在它们的内存被释放之前。

于 2013-02-16T12:34:48.563 回答
2
  • 默认构造函数调用所有成员变量的默认构造函数,不包括原始类型(char、int、指针)。
  • 析构函数可以显式调用,但并不意味着对象的析构。如果对象在堆栈上,那么它不可能对它做任何事情。
  • 默认情况下,析构函数不是虚拟的,但如果您打算从类继承,它们确实应该是虚拟的。
  • 如果对象被释放(超出范围,从堆中删除,或封闭对象以任何方式被销毁),则将调用 desctuctor。
于 2013-02-16T12:24:49.543 回答
1

除了 Notinlist 的回答:

默认构造函数调用你的基类的构造函数。

如果是这样,析构函数不能本质上用作“清除所有数据”类型的函数,并通过清除动态内存分配的变量并重新使用它而不是让计算机找到新的变量来提高代码的某些部分的效率吗?堆上的内存块?

你有点描述一个内存池。如果您愿意,您的对象可能会获取内存缓冲区并将其返回到您发明的某个池系统/从该池系统返回。但在大多数情况下,分配足够快且频率足够低,以至于人们(不再)这样做了。并不是说它们不常见,但它们需要发生很多事情才能注意到对性能的影响。

于 2013-02-16T12:30:51.470 回答
1

手动调用析构函数通常是个坏主意。关于析构函数的 C++ FAQ 部分有很多关于这方面的好信息。

如果您确实想明确地销毁一个对象,您可以使用额外的作用域来安全地调用析构函数(请参阅此 FAQ条目)。此方法还可以防止您使用已销毁的对象实例。尽管该实例似乎可用,但实际上并非如此。

如果您的意图是释放类实例所拥有的一些资源,但不是全部,您可以尝试两件事:

  • 在类上定义一个clear()(或类似的)方法。
  • 确保在clear()调用后保持类的不变量。

假设您最初手动调用析构函数的方法有效,或者您选择执行与上述clear()方法类似的操作,在这两种情况下,您以后都可能遇到问题。

在 C++ 中,一种很好理解且经常实践的资源管理方法是 Resource Acquisition Is Initialization(通常缩写为 RAII,但如果名称混淆,请忽略该名称,这个概念是可以理解的)。有关有用信息,请参阅Wikipedia 文章或此答案

这是 tl;博士虽然:

  • 资源的生命周期应始终与对象的生命周期相关联。
  • 对象的生命周期从构造函数完成时开始
  • 对象的生命周期在析构函数完成时结束。

遵循这个习惯用法通常可以防止 C++ 资源管理问题发生。

于 2013-02-16T12:51:58.590 回答