Virtual destructors
Although C++ provides a default destructor for your classes if you do not provide one yourself, it is sometimes the case that you will want to provide your own destructor (particularly if the class needs to deallocate memory). You should
always make your destructors virtual if you’re dealing with inheritance. Consider the following example:
06 | cout << "Calling ~Base()" << endl; |
10 | class Derived: public Base |
18 | m_pnArray = new int [nLength]; |
23 | cout << "Calling ~Derived()" << endl; |
30 | Derived *pDerived = new Derived(5); |
31 | Base *pBase = pDerived; |
Because pBase is a Base pointer, when pBase is deleted, the program looks to see if the Base destructor is virtual. It’s not, so it assumes it only needs to call the Base destructor. We can see this in the fact that the above example prints:
Calling ~Base()
However, we really want the delete function to call Derived’s destructor (which will call Base’s destructor in turn). We do this by making Base’s destructor virtual:
06 | cout << "Calling ~Base()" << endl; |
10 | class Derived: public Base |
18 | m_pnArray = new int [nLength]; |
23 | cout << "Calling ~Derived()" << endl; |
30 | Derived *pDerived = new Derived(5); |
31 | Base *pBase = pDerived; |
Now this program produces the following result:
Calling ~Derived()
Calling ~Base()
Again, whenever you are dealing with inheritance, you should make your destructors virtual.
Virtual assignment
It is possible to make the assignment operator virtual. However, unlike the destructor case where virtualization is always a good idea, virtualizing the assignment operator really opens up a bag full of worms and gets into some advanced topics outside of the scope of this tutorial. Consequently, we are going to recommend you leave your assignments non-virtual for now, in the interest of simplicity.
Overriding virtualization
Very rarely you may want to override the virtualization of a function. For example, consider the following code:
04 | virtual const char * GetName() { return "Base" ; } |
07 | class Derived: public Base |
10 | virtual const char * GetName() { return "Derived" ; } |
There may be cases where you want a Base pointer to a Derived object to call Base::GetName() instead of Derived::GetName(). To do so, simply use the scope resolution operator:
4 | Base &rBase = cDerived; |
6 | cout << rBase.Base::GetName() << endl; |
You probably won’t use this very often, but it’s good to know it’s at least possible.
The downside of virtual functions
Since most of the time you’ll want your functions to be virtual, why not just make all functions virtual? The answer is because it’s inefficient — resolving a virtual function call takes longer than a resolving a regular one. Furthermore, the compiler also has to allocate an extra pointer for each class object that has one or more virtual functions. We’ll talk about this more in the next couple of lessons.
No comments:
Post a Comment