Inheritance- III


The following topics are covered in this section:


Can a Destructor be Virtual?

We’ve seen about classes and we’ve also dealt with inheritance. But there is one case that we haven’t seen as yet. Let’s go back to the class called ‘car’ and let’s assume that we have one derived class from ‘car’ called ‘ferrari’. If you create an instance of ferrari, then when the ferrari object is destroyed this will invoke 2 destructors (the ‘ferrari’ class destructor and the ‘car’ class destructor).

Suppose we create an object of ‘ferrari’ using the ‘new’ operator and the pointer is of the base class’ type (i.e. the pointer is of type ‘car’). What do you think will happen? Just see the coding below so that you get an idea of the problem:

#include <iostream.h>
class car
{private:
    int speed;
    char color[20];
public:
~car( )
{
cout<<"\nDestroying the Car";
}
};
class ferrari:public car
{public:
ferrari( )
{
cout<<"Creating a Ferrari";
}
~ferrari( )
{
cout<<"\nDestroying the Ferrari";
}
};
int main( )
{car *p = new ferrari;
delete p;
return 0;
}

You can be sure that a new ferrari object has been created. The question is: does the ferrari object really get destroyed?

The output for the above program would be:

Creating a Ferrar
Destroying the Car

Surprised? The ferrari object hasn’t been destroyed though the object was created. The problem is that since we are using a pointer to the base class the compiler tends to only invoke the base destructor when you delete it. How to overcome this problem? This is where we have to make the destructor virtual. By making the destructor virtual, the correct destructor will be called in the program. A destructor is just like any other member function; so to make it virtual just precede the base class destructor by the keyword ‘virtual’. Check out this modified program:

class car
{private:
    int speed;
    char color[20];
public:
virtual ~car( )
{
cout<<"\nDestroying the Car";
}
};
class ferrari:public car
{public:
ferrari( )
{
cout<<"Creating a Ferrari";
}
~ferrari( )
{
cout<<"\nDestroying the Ferrari";
}
};
int main( )
{ car *p = new ferrari;
delete p;
return 0;
}

The output would now be:

Creating a Ferrari
Destroying the Ferrari
Destroying the Car

This is what we call virtual destructor in C++.


Pointers to base and derived classes

“Every Ferrari is a car but not every car is a Ferrari”.

We’ve seen how to create pointers to objects and how to use them to access member functions. Now the question arises what will happen if we create a pointer to an object of base class and then point to an object of a derived class? What if we do the reverse?

Upcasting: a derived class being treated like the base class (going up the hierarchy).

Downcasting: a base class being treated like a derived class (going down the hierarchy).

Consider the following example:

 class car
{
protected:
            int speed;
 
public:
            car( )                            
            {
                        speed=100;
            }
           
            void display( )
            {
                        cout<<"\nSpeed of general car is : "<<speed;
            }
};
 
class ferrari:public car
{
public:
            ferrari( )
            {
                        speed=200;
            }
 
            void display( )
            {
                        cout<<"\nSpeed of ferrari is : "<<speed;
            }
            void change( )
            {
                        speed=speed + 100;
            }
};
 
int main( )
{
car *cp;
car c;
ferrari f;
 
cp=&c;
cp->display( );                          
// Speed of car is : 100
 
cp=&f;                               
        //pointing to derived object
cp->display( );                         
  // Speed of car is : 200
 
cp->change( );                         
  //ERROR – change ( ) not present in class ‘car’
 
return 0;
}

Compiling the code throws an error on the line:

cp->change( );  

The reason for this error is that ‘cp’ is a pointer to the base class. The base class ‘car’ does not have a function change ( ) and so even though ‘cp’ is now pointing to a derived object, it cannot call the change ( ) function of the derived class.

 Remember: A base class pointer cannot access a member which is not present in the base class (even though it points to a derived type). We’ll take a look at the reason for this once we learn about virtual functions.

The term ‘upcasting’ is used when we consider a derived class object as a base class object (we treat a Ferrari as a car; or a teacher as an employee; or a student as a person etc.). Since we are going up the inheritance hierarchy tree (from derived to base), we call this upcasting. This is a very useful feature because any function that operates on the base class will be capable of operating on the derived class as well. Upcasting is what you will come across when we discuss polymorphism using virtual functions. The drawback is that upcasting can lead to object slicing (we’ll take a look at this later).  

The opposite of upcasting is downcasting and it is dangerous. It is dangerous to consider every employee as a teacher or every person as a student because the base class will not have the specializations of the derived class. Downcasting would’ve been acceptable if every person can be considered a student – in that case every person will have properties like student ID number, courses opted for etc. But this isn’t correct because every person is not a student. A person who isn’t a student will not have these attributes. So if we have a member function in class student to display these attributes, then invoking this function on an object of type person will lead to unpredictable results. Thus downcasting is dangerous. 

If we comment out the code:

cp->change( );  

and then compile and execute the above code, we’ll get an interesting output:

Speed of general car is : 100

Speed of general car is : 200

What’s wrong? It’s actually the base class’ display( ) function which has been called twice; the derived class’ display( ) function has never been called. If it were called the output should have been:

Speed of general car is : 100

Speed of ferrari is : 200

Thus we can come to the conclusion:

            cp=&f;  

            cp->display( );  

isn’t performing what we expect. If ‘cp’ is pointing to the ferrari object then it should have used the display( ) function present in the class ferrari!

Another doubt that you might have is: “How is the value of speed correctly displayed in the output?”

Speed of general car is : 100

Speed of general car is : 200

If the pointer ‘cp’ were still pointing to the base class object then it should have displayed:

Speed of general car is : 100

Speed of general car is : 100

But since we have a display of the value 200, it indicates that ‘cp’ does really point to the ferrari object after the statement:

cp=&f;  

The only problem is that the wrong display function is being called. The function is wrong but member data is correct! 

Two parts to this answer:

1.) The concept of early binding decides what definition to be called – the compiler decides at compile time what function definition to be used. The compiler thinks like this: “I don’t see any instruction for late binding and cp is a pointer to type car. So I shall use the display( ) function defined in the base class car”. More on this in the section on virtual functions that follows.

2.) The data is correct because of the way objects are stored in memory. The relative position of the data ‘speed’ in the base class and derived class is the same; so since we changed the address of cp to ‘f’, the member data access will be that of the derived object.


Go back to the Contents Page 2


Copyright © 2004 Sethu Subramanian All rights reserved.