2013-02-16 210 views
1

我在想,如果默认的类析构函数在被调用时实际上做了什么。做默认析构函数做什么?

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

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

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

谢谢。

+0

如果调用析构函数手动释放内存,如果该对象被破坏,然后会发生什么?我不确定你的目标是什么,但它可能更适合简单的成员函数。 – Bingo 2013-02-16 12:24:51

+0

回答这个问题可能会有所帮助:HTTP://stackoverflow.com/questions/1036019/does-calling-a-destructor-explicitly-destroy-an-object-completely – 2013-02-16 12:30:31

+0

考虑使用内存池或放置新/删除,而不是这种优化。请记住:“不成熟的优化是万恶之源”。 – doc 2013-02-16 13:08:02

回答

2
  • 默认构造函数调用所有成员变量的默认构造函数,不包括原始类型(char,int,pointers)。
  • 可以显式调用析构函数,但它并不意味着对象的解除分配。如果对象在堆栈上,那么它不可能对它做任何事情。
  • 析构函数在默认情况下不是虚拟的,但如果您计划从类继承,它们确实应该是虚拟的。
  • 如果对象被释放(超出范围,从堆中删除或封闭对象被任何方式解析),将调用内部函数。
+0

我的意思是'虚拟'是每个班级默认都会得到一个(可以称之为),但它什么都不做;我的意思并不是继承:)。但是这是否意味着类的任何成员变量是结构体或类都将调用它们的析构函数? – Edward 2013-02-16 13:22:38

+0

@爱德华是的。总是隐含的。如果你真的为你的课程写了一个析构函数,你仍然不需要调用你的成员的描述符。你只需要关心'删除'动态分配的对象,关闭打开的文件并释放与你的对象相关的其他资源。如果你没有这样的东西,那么你不需要写一个析构函数。 – Notinlist 2013-02-16 13:46:07

+1

有关继承的一点是误导性的:使用非虚拟析构函数的基类是完全安全的。当析构函数应该是虚拟的唯一时间是当你打算在运行时多态性中使用基类的静态类型时(换句话说,当你打算通过指针访问具有动态类型“派生类”的实例时/(类型“基类”的非const)引用)。 – 2013-02-16 13:46:51

1

除了Notinlist的回答是:

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

如果是这样,不能析构函数本质上可以作为一个“所有数据清除” 样的功能,并通过 使代码更高效一些地区清除动态内存分配的变量,并重新使用它 而不是让计算机在 堆上找到新的内存块?

你有点描述内存池。如果你愿意,你的对象可以获取内存缓冲区并从你创建的某个池系统返回内存缓冲区。但是,大多数情况下,分配速度足够快并且不够频繁,以至于人们无法做到这一点。不是说他们很少,但他们需要发生很多注意到性能的影响。

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; 
} 

You'll see,调用B的默认析构函数调用A的析构函数,因为B包含A

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

只有当b超出范围是堆栈展开并且合成B::~B()调用,并且依次为01在他们的记忆被释放之前,它们是。

+0

您的输出与您的代码(f()vs〜B())不匹配。 – Kleist 2013-02-16 13:05:59

+0

谢谢@Kleist,我简化了代码,但忘记更新输出。 http://ideone.com/8fxvWQ显示它按照说明运行。 – Johnsyweb 2013-02-16 13:09:57

1

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

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

如果你的目的是要释放一些,但不是全部,一个类的实例所拥有的资源,你可以尝试两件事情:

  • 定义在类clear()(或类似)的方法。
  • 确保clear()被称为后级的不变维持。

假设你最初的方法来手动调用析构函数的工作,或者你选择这样做,像上面的方法clear()的东西,在这两种情况下,你以后可能会碰到的问题。

一个很好的理解和常练在C++中的资源管理的方法是资源获取就是初始化(通常缩写RAII,却忽略了名字,如果它是混乱的,这个概念是可以理解的)。见this维基百科的文章或本answer有用的信息。

这里是TL;博士虽然:

  • 资源的寿命应该总是被绑定到的 对象的寿命。
  • 对象的生命周期在构造函数完成时开始
  • 对象的生命周期在析构函数完成时结束。

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

+0

我仍然不确定为什么明确调用析构函数是一个坏主意。如果析构函数在删除变量(如指针)(例如,如果(指针!= nullptr)删除指针;)之前有保护,则肯定它不应该是有害的。这是否仅仅是为了防止程序员忘记析构函数默认的东西和不被销毁的东西(例如成员变量或将被析构函数调用的继承类)? – Edward 2013-02-16 13:42:05

+0

@爱德华不,那是完全错误的。首先,那个守卫不会做任何事*,这没用。其次,即使是有效的警卫也不会保护 - 即使是一个默认的析构函数也不能多次调用,因为它会调用所有成员变量的析构函数(参见Notinlist的答案)。最后,你可以手动调用析构函数,但是你必须非常小心,因为就像我说过的,它不能被重复调用析构函数,所以你必须防止任何后续的*自动*调用到它。 – 2013-02-16 13:50:35

相关问题