Date: | May 7, 2004 / year-entry #178 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20040507-00/?p=39443 |
Comments: | 20 |
Summary: | When should your C++ object's destructor be virtual? First of all, what does it mean to have a virtual destructor? Well, what does it mean to have a virtual method? If a method is virtual, then calling the method on an object always invokes the method as implemented by the most heavily derived class. If... |
When should your C++ object's destructor be virtual? First of all, what does it mean to have a virtual destructor? Well, what does it mean to have a virtual method? If a method is virtual, then calling the method on an object always invokes the method as implemented by the most heavily derived class. If the method is not virtual, then the implementation corresponding to the compile-time type of the object pointer. For example, consider this: class Sample { public: void f(); virtual void vf(); }; class Derived : public Sample { public: void f(); void vf(); } void function() { Derived d; Sample* p = &d; p->f(); p->vf(); } The call to On the other hand, the call to The call to Okay, you knew that. Virtual destructors work exactly the same way. It's just that you rarely invoke the destructor explicitly. Rather, it's invoked when an automatic object goes out of scope or when you void function() { Sample* p = new Derived; delete p; } Since Armed with this information, you can now answer the question. A class must have a virtual destructor if it meets both of the following criteria:
Some people say that you need a virtual destructor if and only if you have a virtual method. This is wrong in both directions. Example of a case where a class has no virtual methods but still needs a virtual destructor: class Sample { }; class Derived : public Sample { CComPtr<IStream> m_p; public: Derived() { CreateStreamOnHGlobal(NULL, TRUE, &m_p); } }; Sample *p = new Derived; delete p; The And here's an example of a case where a class has virtual methods but does not require a virtual destructor. class Sample { public: virtual void vf(); } class Derived : public Sample { public: virtual void vf(); } Derived *p = new Derived; delete p; Since the object deletion occurs from the pointer type that matches the type of the actual object, the correct destructor will be invoked. This pattern happens often in COM objects, which expose several virtual methods corresponding to its interfaces, but where the object itself is destroyed by its own implementation and not from a base calss pointer. (Notice that no COM interfaces contain virtual destructors.) The problem with knowing when to make your destructor virtual or not is that you have to know how people will be using your class. If C++ had a "sealed" keyword, then the rule would be simpler: If you do a " |
Comments (20)
Comments are closed. |
"Some people say that you need a virtual destructor if and only if you have a virtual method. This is wrong in both directions."
Well, almost.
If the sizeof or layout of your class is a concern for some reason, making the destructor virtual "costs nothing" if your base class already has a virtual method. You’re already carrying a vptr at that point.
There’s rarely a reason NOT to make the destructor virtual if you already have a virtual method, I think. (I guess it’s possible you’re going to create a LOT of temporary Derived’s, so the ~Derived virtual call becomes a bottleneck, but that seems very unlikely…)
An empty virtual destructor for your last sample class would hardly be a maintenance headache, and opens the possibility for the use case to change down the road without base class changes.
If you really don’t want a virtual destructor, you should probably make the base class’ destructor protected, to prevent mistakes.
Also, there’s a small mistake – Derived isn’t derived in the last bit of code.
Fixed, thanks Daniel.
Mike G.: Most COM objects do not have a virtual destructor since they are destructed from the derived class (inside the Release() methods). Yes, it doesn’t cost you much (an etra pointer in the vtable), but if your object architecture already encapsulates object destruction in a virtual method (like COM does), then virtualizing the destructor may end up creating confusion.
I can’t think of one offhand.
Marc Wallace: You might want to do that if you’re testing the debug heap’s memory-leak detection.
"There’s rarely a reason NOT to make the destructor virtual if you already have a virtual method, I think. (I guess it’s possible you’re going to create a LOT of temporary Derived’s, so the ~Derived virtual call becomes a bottleneck, but that seems very unlikely…) "
One reason is that it needn’t be necessary.
Consider for example:
struct Base
{
arbitraryType arbitraryMemberVariable;
virtual void doSomething() = 0;
~Base() {}
};
struct Derived : Base
{
arbitraryType arbitraryMemberVariable;
virtual void doSomething() { blahblahblah; }
~Derived() {}
};
// any old block for any old reason
{
const Base& b((Derived()));
}
In this case the Derived d-tor will be called in spite of being bound to a reference to Base, because the compiler uses the static type of the temporary to figure out which destructor to call.
Esoteric? Sure. Useless? Hell no, as anyone who’s seen ScopeGuard will know.
When should your destructor be virtual?…
Slight inaccuracy:
The statement
The delete p will invoke Sample::~Sample instead of Derived::~Derived, resulting in a leak of the stream m_p.
is wrong. If a delete expression is evaluated on a base class pointer that points to an instance of a derived class, the behavior is undefined; anything can happen.
It is possible that the base class destructor is invoked and the derived clas destructor isn’t, but that’s just an instance of undefined behavior. Other instances are a program crash or the computer catching fire.
Ah, excellent point Thomas.
5.3.5.3: "If the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined."
Are there any cases where you would *not* want to invoke the destructor of the most derived class that has it?
In other words: somewhere, something knows that p isa Derived. Are there cases when Derived::~Derived() is defined, but you would actually want "delete p" to do a Simple::~Simple()?
There you go giving away my interview questions again, Raymond.
Don’t click on the link in Gary Niger’s post. It’s a GNAA troll link. Like goatse.cx only a thousand times worse.
Thanks, Mr. Troll Buster. I deleted the comment. It was also a clipboard stealer. Whatever text was on your clipboard got uploaded to the site, and then you got redirected to something unpleasant…
Hi,
I think the rule about "When should your destructor be virtual? " can be:
The DTOR of base class is:
– public and MUST be virtual
– protected and then should be non-virtual
And by the way, is not a good ideea to inherit public from a class without any virtual method.
Wow, a clipboard stealer… never heard of that one before. Amazing the things that are possible.
I wonder if we could perform a DNS attack on him by copying gigantic bitmaps into our clipboard, then visiting his site :)
Graphics means use of graphics function
like circle(),line() etc., like Turbo c++.
Yupp, it means drawing objects using graphics functions using standard graphics library as well as using user defined classes which may or may not be inherited from the standard classes.
Commenting on this article has been closed.
PingBack from http://kiranu.wordpress.com/2006/07/06/why-virtual-destructors/