虚拟析构函数:非动态分配内存时需要吗?
如果我的类不动态分配任何内存,我们是否需要虚拟析构函数?
例如,
class A
{
private:
int a;
int b;
public:
A();
~A();
};
class B: public A
{
private:
int c;
int d;
public:
B();
~B();
};
在这种情况下,我们需要将 A 的析构函数标记为 virtual 吗?
Do we need a virtual destructor if my classes do not allocate any memory dynamically ?
e.g.
class A
{
private:
int a;
int b;
public:
A();
~A();
};
class B: public A
{
private:
int c;
int d;
public:
B();
~B();
};
In this case do we need to mark A's destructor as virtual ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
问题不在于您的类是否动态分配内存。如果类的用户通过 A 指针分配 B 对象,然后将其删除:
在这种情况下,如果 A 没有虚拟析构函数,则 C++ 标准表示您的程序表现出未定义的行为。这不是什么好事。
此行为在标准第 5.3.5/3 节中指定(此处指
删除
):The issue is not whether your classes allocate memory dynamically. It is if a user of the classes allocates a B object via an A pointer and then deletes it:
In this case, if there is no virtual destructor for A, the C++ Standard says that your program exhibits undefined behaviour. This is not a good thing.
This behaviour is specified in section 5.3.5/3 of the Standard (here referring to
delete
):虚拟析构函数的目的(即使析构函数虚拟的目的)是为了通过删除表达式促进对象的多态删除。如果您的设计不要求对象的多态删除,则不需要虚拟析构函数。参考您的示例,如果您必须通过
A *
类型的指针删除B
类型的对象(多态删除),则需要虚拟析构函数在层次结构中与A
一样高。从正式的角度来看就是这样。(请注意,顺便说一句,正如尼尔所说,重要的是如何创建/删除类对象,而不是类如何管理其内部内存。)
至于良好的编程实践......这取决于您的意图和设计结尾。如果您的类根本没有被设计为多态(没有任何虚拟方法),那么您不需要虚拟析构函数。如果您的类是多态的(至少有一个虚拟方法),那么“以防万一”将析构函数设为虚拟可能是一个非常好的主意,在这种情况下,它的性能/内存损失几乎为零。
后者通常被表示为一个相当著名的良好实践指南:如果您的类至少有一个虚拟方法,则也将析构函数设为虚拟。尽管从正式的角度来看,那里可能并不真正需要虚拟析构函数,但它仍然是一个非常好的遵循指南。
没有资源但可以形成多态层次结构的类应该始终定义空的虚拟析构函数,除非在层次结构的最底层定义一个显式的空(甚至纯)虚拟析构函数就足够了。所有其他析构函数将自动变为虚拟,即使它们是由编译器隐式定义的。即,您不必在每个类中显式定义一个空的析构函数。只要有基础就足够了。
The purpose of virtual destructor (i.e. the purpose of making a destructor virtual) is to facilitate the polymorphic deletion of objects through delete-expression. If your design does not call for polymorphic deletion of objects, you don't need virtual destructors. Referring to your example, if you'll ever have to delete an object of type
B
through a pointer of typeA *
(polymorphic deletion), you'll need virtual destructor as high up in the hierarchy asA
. That's how it looks from a formal point of view.(Note, BTW, as Neil said, that what's important is how you create/delete your class objects, not how classes manage their internal memory.)
As for the good programming practices... It depends on your intent and your design in the end. If your classes are not designed to be polymorphic at all (no virtual methods whatsoever), then you don't need virtual destructors. If your class is polymorphic (have at least one virtual method), then making the destructor virtual "just in case" might be a very good idea, and in this case it bears virtually zero performance/memory penalty with it.
The latter is usually expressed as a rather well-known good practice guideline: if your class has at least one virtual method, make the destructor virtual as well. Although from the formal point of view a virtual destructor might not be really needed there, it is still a pretty good guideline to follow.
Classes that have no resources but can form polymorphic hierarchies should always define empty virtual destructors, except that it is perfectly sufficient to define an explicit empty (and even pure) virtual destructor at the very base of the hierarchy. All other destructors will become virtual automatically, even if they are defined implictly by the compiler. I.e. you don't have to explicitly define an empty destructor in every class. Just the base is enough.
释放内存并不是析构函数可以执行的唯一关键功能。例如,它还可以用于重置全局状态。不这样做不会泄漏内存,但可能会导致程序中出现其他问题。
此外,即使您的析构函数今天没有做任何有用的事情,它也可能在将来的某个时候做。如果您有继承,就没有真正的理由避免使用虚拟析构函数,那么为什么不添加它并在晚上睡得更好呢?
Freeing memory is not the only critical function a destructor can perform. It can also be used to reset global state for instance. Not doing this won't leak memory but could potentially cause other issues in your program.
Additionally, even if your destructor doesn't do anything useful today, it may at some point in the future. There's no real reason to avoid a virtual destructor if you have inheritance so why not just add it and sleep better at night?
父类的析构函数总是自动调用,并且如果没有显式声明 dtor,则始终生成默认 dtor。在您的示例中,A 和 B 都不需要有一个不平凡的 dtor。
如果您的类具有虚拟函数,那么额外的虚拟 dtor 不会有什么坏处,并且是很好的做法。如果您的类分配内存或任何其他资源(例如打开文件),则需要 dtor 在销毁时再次释放该资源。
The destructor of the parent class is always automatically called, and the default dtor is always generated if there's no explicit dtor declared. In your example, neither A nor B needs to have a non-trivial dtor.
If you class has virtual functions, an additional virtual dtor doesn't hurt, and is good practice. In case you class allocates memory or any other resource (like opening a file), a dtor is needed to free that resource again upon destruction.
将析构函数声明为 virtual 的目的是,每当您对指向 Derived 类型对象的 Base 类型指针调用 delete 时,都能够调用派生类的析构函数。不这样做会导致未定义的行为。
如果不动态分配内存,则不需要将析构函数标记为虚拟,这一假设意味着如果不动态分配内存,则不需要调用派生类析构函数,这是错误的。因为除了释放动态分配的内存之外,您仍然可以在派生类的析构函数中执行其他一些操作。例如关闭打开的文件、记录一些信息等。
The purpose of declaring destructor as virtual is to be able to invoke the derived class's destructor whenever you call delete on a pointer of type Base which is pointing to object of type Derived. Not doing so would result in undefined behavior.
The assumption that you need not mark destructor as virtual if you are not allocating memory dynamically implies that you do not need to call derived class destructor if you are not allocating memory dynamically, which is wrong. As you may still do several other operations in your derived class's destructor other than just deallocating the dynamically allocated memory. Examples would be closing an open file, logging some information etc.
如果您唯一关心的是内存,也许您应该从保护基类析构函数(和/或其他析构函数)开始。然后,如果某些内容无法编译,您就会明白原因。参考:boost::任何方式。
If your only concern is memory, maybe you should start by protecting base class destructor (and/or maybe others). Then if something does not compile, you'll see why. Ref: boost::any ways.