Skip to main content
GameDev.net gamedev.net
🔒 Locked

Virtual Destructor

Started by CollegeDosser Nov 26, 2003 at 12:45 PM 11 replies 1.4k views
Original Post
CollegeDosser
CollegeDosser
In the Game Programming All In One book, the author uses virtual destructor''s in the programs in which he demonstrates Polimorphism and inheritence, I understand that if a derived class allocates memory using new, then the destructor must be virtual, however this is not the case in these examples, is he doing it merely because it''s something that you will more than likely have to do when using the above mentioned techniques? Or is there a reason i am unaware of?
Sneftel
Sneftel
He''s doing it because a lot of people do it out of habit. It makes one less thing to remember. And the only actual overhead if you never use it is four bytes per object.... not especially heavy.


"Sneftel is correct, if rather vulgar." --Flarelocke
sbennett
sbennett
Consider the following:

class Base
{
public:
Base();
virtual ~Base();
};

class Derived : public Base
{
public:
Derived();
virtual ~Derived();
};

int main()
{
Base *p = new Derived();
delete p;
}


In this case, the call to new will result in calling Base''s constructor, then Derived''s constructor. The corresponding call to delete will call Derived''s destructor, then Base''s destructor.

However, if Base''s destructor were not declared as virtual, then the delete statement would not call Derived''s destructor. If Derived had any resources that needed to be freed in the destructor, they wouldn''t be.

In short, if anybody is going to be deriving classes from your base class, make sure it has a virtual destructor.
Sneftel
Sneftel
quote:
Original post by Anonymous Poster
Thats very implementation specific though

What''s your point? That someone, somewhere, is writing a compiler that somehow uses up THREE JIGABYTES OF RAM for each virtual object?


"Sneftel is correct, if rather vulgar." --Flarelocke
Ready4Dis
Ready4Dis
quote:
Original post by Sneftel
quote:
Original post by Anonymous Poster
Thats very implementation specific though

What''s your point? That someone, somewhere, is writing a compiler that somehow uses up THREE JIGABYTES OF RAM for each virtual object?


"Sneftel is correct, if rather vulgar." --Flarelocke


HAHAHAHA, yeah seriously, even if the compiler was a 64-bit one, that had 64-bit addresses, that''s still only 8-bytes. If you''re writing something that handles so many objects that 8-bytes per object is going to kill it, you wouldn''t be asking these questions, because you''d have to have a much better understanding in the first place to write such a program.
browny
browny
It''s not very clear of what you are actually looking for... but the basic rule is: whenever you make a "class" which will be derived you should ALWAYS have your destructors virtual. The reason is as follows:

suppose you have a class "CBase"
and a derived class "CDerived:CBase"

and suppose you have a linked-list of "CBase" object type.. which can well point to "CDerived" type object as well....

e.g

CBase *pLinkedList

With such arrangement.. if you release all the nodes of the linked-list ( i.e by calling "delete" at each node ) ... the desctrutors will be called in the correct sequence.

So, if a pointer (even of CBase * type) is deleted which actually points to a CDerived object... first the CDerived::~CDerived will be called and then CBase:~CBase is called. And this whole thing is handled by C++ automatically.

And here is the rule of the thumb: if you ever have a class which might be derived... always make the destrutor of the base class "virtual"


Z
hplus0603
hplus0603
quote:

And the only actual overhead if you never use it is four bytes per object....



That''s not true. If the object already has a vtable for some other reason (virtual functions, say, which is a major reason to use inheritance in the first place) then making the destructor virtual costs one pointer (possibly two, depending on "this" pointer offset implementation) PER CLASS; amortized on all the objects of that class. VTables are per-class data; vtable pointers are per-object data.

Thus, it''s virtually free for all but the most byzantine edge cases.

Note: GCC uses one pointer and one integer per virtual function, to properly adjust the "this" pointer when using multiple inheritance. MSVC instead uses static typing at the site of the call, so its vtables can be packed tighter.
enum Bool { True, False, FileNotFound };
Shannon Barber
Shannon Barber
The rule of thumb is if you have any virtual member functions, make the dtor virtual as well. In C++0x this will be automatic (as pointed out, it cost nothing once you already have another virtual method).


quote:

Or is there a reason i am unaware of?


It's arguably safer, and doesn't cost you anything.

...
quote:
Original post by hplus0603
quote:

And the only actual overhead if you never use it is four bytes per object....



That's not true. VTables are per-class data; vtable pointers are per-object data.



The most likely size of a vtable pointer is 4 bytes. You are both correct.

[edited by - Magmai Kai Holmlor on November 26, 2003 7:49:58 PM]
The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
dot
dot
quote:
Original post by Magmai Kai Holmlor
The rule of thumb is if you have any virtual member functions, make the dtor virtual as well. In C++0x this will be automatic (as pointed out, it cost nothing once you already have another virtual method).


Automatic? So is virtual ~dtor() depreciated? As in, what if there''s some obscure reason I only need a virtual destructor and no virtual functions?(I haven''t thought about when I will only do that, but just a wondering thought) Can I still use virtual ~dtor() ?

BrianL
BrianL
Using virtual destructors in base classes is very important.

To keep this all clear, for instance:


class Base
{
~Base(){}
};

class Derived : public Base
{
Derived(int i) { m_pBuffer = new int[i]; }
~Derived()
{
if (m_pBuffer)
{
delete[] m_pBuffer;
m_pBuffer = NULL;
}
}
}

main()
{
Base* pBase = new Derived;
delete pBase;
}


Now, as Bases destructor is not virtual, then Derived destructor would not be called on deletion. This would result in the memory the derived class allocated not being freed.

For this reason, it is considered good form to give any class which will be derived from a virtual destructor. Until the next iteration of the language (which we probably won't have compilers for until ... well, a long time from now) making destructors in base classes virtual is important.

The reason this is important to do in all cases is that when a base class is created, the derived class can do anything it wants. The writer of the derived class will expect their destructor to be called. Construction and destruction should mirror each other, which fails to happen if the base class destructor is non virtual.


[edited by - BrianL on November 26, 2003 9:34:23 PM]
SiCrane
SiCrane
quote:
Original post by dot
quote:
Original post by Magmai Kai Holmlor
The rule of thumb is if you have any virtual member functions, make the dtor virtual as well. In C++0x this will be automatic (as pointed out, it cost nothing once you already have another virtual method).


Automatic? So is virtual ~dtor() depreciated? As in, what if there''s some obscure reason I only need a virtual destructor and no virtual functions?(I haven''t thought about when I will only do that, but just a wondering thought) Can I still use virtual ~dtor() ?


You should still be able to use virtual ~dtor().

An example of a base class that uses a virtual destructor with no other virtual functions is the SmallObject class used in loki. In that case it uses the whole vtable scaffolding in order to guarantee that the correct size is passed during the call of SmallObject::delete().

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.