C++ 什么时候不应该使用虚拟析构函数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/300986/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
When should you not use virtual destructors?
提问by Mag Roader
Is there ever a good reason to notdeclare a virtual destructor for a class? When should you specifically avoid writing one?
是否有充分的理由不为类声明虚拟析构函数?你什么时候应该特别避免写一个?
采纳答案by sep
There is no need to use a virtual destructor when any of the below is true:
当以下任一情况为真时,无需使用虚拟析构函数:
- No intention to derive classes from it
- No instantiation on the heap
- No intention to store in a pointer of a superclass
- 无意从中派生类
- 堆上没有实例化
- 无意存储在超类的指针中
No specific reason to avoid it unless you are really so pressed for memory.
没有特别的理由去避免它,除非你真的很想记。
回答by Richard Corden
To answer the question explicitly, i.e. when should you notdeclare a virtual destructor.
要明确地回答这个问题,即什么时候应该不声明一个虚析构函数。
C++ '98/'03
C++'98/'03
Adding a virtual destructor might change your class from being POD (plain old data)* or aggregate to non-POD. This can stop your project from compiling if your class type is aggregate initialized somewhere.
添加虚拟析构函数可能会将您的类从POD(纯旧数据)* 或聚合更改为非 POD。如果您的类类型在某处聚合初始化,这可能会阻止您的项目编译。
struct A {
// virtual ~A ();
int i;
int j;
};
void foo () {
A a = { 0, 1 }; // Will fail if virtual dtor declared
}
In an extreme case, such a change can also cause undefined behaviour where the class is being used in a way that requires a POD, e.g. passing it via an ellipsis parameter, or using it with memcpy.
在极端情况下,此类更改还可能导致未定义行为,其中以需要 POD 的方式使用类,例如通过省略号参数传递它,或将它与 memcpy 一起使用。
void bar (...);
void foo (A & a) {
bar (a); // Undefined behavior if virtual dtor declared
}
[* A POD type is a type that has specific guarantees about its memory layout. The standard really only says that if you were to copy from an object with POD type into an array of chars (or unsigned chars) and back again, then the result will be the same as the original object.]
[* POD 类型是一种对其内存布局有特定保证的类型。该标准实际上只是说,如果您要从具有 POD 类型的对象复制到字符数组(或无符号字符)并再次返回,则结果将与原始对象相同。]
Modern C++
现代 C++
In recent versions of C++, the concept of POD was split between the class layout and its construction, copying and destruction.
在 C++ 的最新版本中,POD 的概念被分为类布局及其构造、复制和销毁。
For the ellipsis case, it is no longer undefined behavior it is now conditionally-supported with implementation-defined semantics (N3937 - ~C++ '14 - 5.2.2/7):
对于省略号的情况,它不再是未定义的行为,现在有条件地支持实现定义的语义 (N3937 - ~C++ '14 - 5.2.2/7):
...Passing a potentially-evaluated argument of class type (Clause 9) having a non-trivial copy constructor, a non-trivial move constructor, or a on-trivial destructor, with no corresponding parameter, is conditionally-supported with implementation-defined semantics.
...传递具有非平凡复制构造函数、非平凡移动构造函数或非平凡析构函数的类类型(第 9 条)的潜在评估参数,没有相应的参数,有条件地支持实现-定义的语义。
Declaring a destructor other than =default
will mean it's not trivial (12.4/5)
声明一个析构函数而不是=default
意味着它不是微不足道的 (12.4/5)
... A destructor is trivial if it is not user-provided ...
...如果析构函数不是用户提供的,则它是微不足道的...
Other changes to Modern C++ reduce the impact of the aggregate initialization problem as a constructor can be added:
Modern C++ 的其他更改减少了聚合初始化问题的影响,因为可以添加构造函数:
struct A {
A(int i, int j);
virtual ~A ();
int i;
int j;
};
void foo () {
A a = { 0, 1 }; // OK
}
回答by Andy
I declare a virtual destructor if and only if I have virtual methods. Once I have virtual methods, I don't trust myself to avoid instantiating it on the heap or storing a pointer to the base class. Both of these are extremely common operations and will often leak resources silently if the destructor is not declared virtual.
当且仅当我有虚方法时,我才声明一个虚析构函数。一旦我有了虚拟方法,我就不相信自己会避免在堆上实例化它或存储指向基类的指针。这两个都是非常常见的操作,如果析构函数没有声明为 virtual,它们通常会悄悄地泄漏资源。
回答by Jay Conrod
A virtual destructor is needed whenever there is any chance that delete
might be called on a pointer to an object of a subclass with the type of your class. This makes sure the correct destructor gets called at run time without the compiler having to know the class of an object on the heap at compile time. For example, assume B
is a subclass of A
:
只要有delete
可能在指向具有您的类的类型的子类的对象的指针上调用任何机会,就需要虚拟析构函数。这确保在运行时调用正确的析构函数,而编译器不必在编译时知道堆上对象的类。例如,假设B
是 的子类A
:
A *x = new B;
delete x; // ~B() called, even though x has type A*
If your code is not performance critical, it would be reasonable to add a virtual destructor to every base class you write, just for safety.
如果您的代码对性能不是很关键,那么为了安全起见,为您编写的每个基类添加一个虚拟析构函数是合理的。
However, if you found yourself delete
ing a lot of objects in a tight loop, the performance overhead of calling a virtual function (even one that's empty) might be noticeable. The compiler cannot usually inline these calls, and the processor might have a difficult time predicting where to go. It is unlikely this would have a significant impact on performance, but it's worth mentioning.
但是,如果您发现自己delete
在一个紧密循环中调用了很多对象,则调用虚函数(即使是空函数)的性能开销可能会很明显。编译器通常无法内联这些调用,并且处理器可能很难预测去哪里。这不太可能对性能产生重大影响,但值得一提。
回答by mxcl
Virtual functions mean every allocated object increases in memory cost by a virtual function table pointer.
虚函数意味着每个分配的对象都会增加一个虚函数表指针的内存成本。
So if your program involves allocating a very large number of some object, it would be worth avoiding all virtual functions in order to save the additional 32 bits per object.
因此,如果您的程序涉及分配大量某个对象,则值得避免使用所有虚函数,以便为每个对象节省额外的 32 位。
In all other cases, you will save yourself debug misery to make the dtor virtual.
在所有其他情况下,您将省去调试痛苦以使 dtor 虚拟化。
回答by Steve Jessop
Not all C++ classes are suitable for use as a base class with dynamic polymorphism.
并非所有 C++ 类都适合用作具有动态多态性的基类。
If you want your class to be suitable for dynamic polymorphism, then its destructor must be virtual. In addition, any methods which a subclass could conceivably want to override (which might mean all public methods, plus potentially some protected ones used internally) must be virtual.
如果你想让你的类适合动态多态,那么它的析构函数必须是虚拟的。此外,子类可能想要覆盖的任何方法(这可能意味着所有公共方法,以及可能在内部使用的一些受保护的方法)都必须是虚拟的。
If your class is not suitable for dynamic polymorphism, then the destructor should not be marked virtual, because to do so is misleading. It just encourages people to use your class incorrectly.
如果您的类不适合动态多态,则不应将析构函数标记为虚拟,因为这样做会产生误导。它只会鼓励人们错误地使用您的课程。
Here's an example of a class which would not be suitable for dynamic polymorphism, even if its destructor were virtual:
这是一个不适合动态多态的类的例子,即使它的析构函数是虚拟的:
class MutexLock {
mutex *mtx_;
public:
explicit MutexLock(mutex *mtx) : mtx_(mtx) { mtx_->lock(); }
~MutexLock() { mtx_->unlock(); }
private:
MutexLock(const MutexLock &rhs);
MutexLock &operator=(const MutexLock &rhs);
};
The whole point of this class is to sit on the stack for RAII. If you're passing around pointers to objects of this class, let alone subclasses of it, then you're Doing It Wrong.
这个课程的重点是坐在 RAII 的堆栈上。如果您传递指向此类对象的指针,更不用说它的子类了,那么您就做错了。
回答by kidfisto
A good reason for not declaring a destructor as virtual is when this saves your class from having a virtual function table added, and you should avoid that whenever possible.
不将析构函数声明为虚拟的一个很好的理由是,这可以使您的类免于添加虚拟函数表,并且您应该尽可能避免这种情况。
I know that many people prefer to just always declare destructors as virtual, just to be on the safe side. But if your class does not have any other virtual functions then there is really, really no point in having a virtual destructor. Even if you give your class to other people who then derive other classes from it then they would have no reason to ever call delete on a pointer that was upcast to your class - and if they do then I would consider this a bug.
我知道很多人更喜欢总是将析构函数声明为虚拟的,只是为了安全起见。但是如果你的类没有任何其他虚函数,那么拥有一个虚析构函数就真的没有意义了。即使您将您的课程提供给其他人,然后他们从中派生出其他课程,那么他们也没有理由对向上转换到您的课程的指针调用 delete - 如果他们这样做了,那么我会认为这是一个错误。
Okay, there is one single exception, namely if your class is (mis-)used to perform polymorphic deletion of derived objects, but then you - or the other guys - hopefully know that this requires a virtual destructor.
好的,有一个例外,即如果您的类被(错误地)用于执行派生对象的多态删除,但是您 - 或其他人 - 希望知道这需要一个虚拟析构函数。
Put another way, if your class has a non-virtual destructor then this is a very clear statement: "Don't use me for deleting derived objects!"
换句话说,如果你的类有一个非虚析构函数,那么这是一个非常明确的声明:“不要用我来删除派生对象!”
回答by Mark Ransom
If you have a very small class with a huge number of instances, the overhead of a vtable pointer can make a difference in your program's memory usage. As long as your class doesn't have any other virtual methods, making the destructor non-virtual will save that overhead.
如果您有一个包含大量实例的非常小的类,则 vtable 指针的开销可能会对程序的内存使用产生影响。只要您的类没有任何其他虚拟方法,将析构函数设为非虚拟就可以节省开销。
回答by J?rn Jensen
I usually declare the destructor virtual, but if you have performance critical code that is used in an inner loop, you might want to avoid the virtual table lookup. That can be important in some cases, like collision checking. But be careful about how you destroy those objects if you use inheritance, or you will destroy only half of the object.
我通常将析构函数声明为虚拟,但如果您有在内循环中使用的性能关键代码,您可能希望避免虚拟表查找。这在某些情况下可能很重要,例如碰撞检查。但是如果使用继承,请注意如何销毁这些对象,否则只会销毁对象的一半。
Note that the virtual table lookup happens for an object if anymethod on that object is virtual. So no point in removing the virtual specification on a destructor if you have other virtual methods in the class.
请注意,如果该对象上的任何方法是虚拟的,则会针对该对象进行虚拟表查找。因此,如果类中有其他虚拟方法,则删除析构函数上的虚拟规范毫无意义。
回答by Lisa
If you absolutely positively must ensure that your class does not have a vtable then you must not have a virtual destructor as well.
如果您绝对肯定必须确保您的类没有 vtable,那么您也不能有虚拟析构函数。
This is a rare case, but it does happen.
这是一种罕见的情况,但确实会发生。
The most familiar example of a pattern that does this are the DirectX D3DVECTOR and D3DMATRIX classes. These are class methods instead of functions for the syntactic sugar, but the classes intentionally do not have a vtable in order to avoid the function overhead because these classes are specifically used in the inner loop of many high-performance applications.
执行此操作的模式的最常见示例是 DirectX D3DVECTOR 和 D3DMATRIX 类。这些是语法糖的类方法而不是函数,但是为了避免函数开销,这些类故意没有 vtable,因为这些类专门用于许多高性能应用程序的内部循环。