Lab Sheet 7
Understanding the Concept of Virtual Base Class, Virtual Function, and RTTI
1. Virtual Base Class
Consider inheritance of the following type.
This type of inheritance is also called multipath inheritance as the Child gets the properties of G-Parent through two paths, Parent-1 and Parent-2. In this type of inheritance, there may be ambiguity in the members of the derived class child because it is derived from two base classes, which are again derived from the same base class. Hence, to avoid this ambiguity, the class G-Parent can be specified as a virtual base class during inheritance.
For example
class Parent1 : virtual public GParent { //... };
class Parent2 : virtual public GParent { //... };
class Child : public Parent1, public Parent2 { //... };
2. Virtual Function
The overridden function in the derived class can be invoked by means of a base class pointer if the function is declared virtual in the base class. A base class pointer can hold the address of a derived class object because the type of every derived class is also a base type. When a virtual function is called through such a pointer, the derived class version is invoked based on the actual object type at runtime.
If a function is defined as a virtual function in the base class and overridden (a function with the same name and parameters) is defined in the derived class and that function is called through the pointer of the base class, then the respective function of the object pointed to by the base class pointer is called. Suppose a virtual function get() is defined in the base class Base and is overridden in the derived class Derived. We can use the base class pointer to invoke the function get() of the derived class object or base class object, whichever the pointer points to. This behavior, where the function call is resolved at runtime based on the actual object type rather than the pointer type, is called runtime polymorphism.
For example
class Base {
public:
virtual void get() // virtual function
{
//...
}
};
class Derived: public Base {
public:
void get() // function overriding
{
//...
}
};
Derived d;
Base *b;
b=&d;
b->get(); //calls get() of the derived class.
3. Pure Virtual Function and Abstract Class
A pure virtual function is a virtual function that has no definition in the base class. It is declared by assigning 0 to it in the class declaration:
class Base {
public:
virtual void display() = 0; // pure virtual function
};
A class that contains at least one pure virtual function is called an abstract class. An abstract class cannot be instantiated directly, that is, we cannot create objects of an abstract class. Its only purpose is to act as a base class that defines a common interface, which derived classes must implement.
Base b; // ERROR: cannot instantiate abstract class
Any class derived from an abstract class must override all its pure virtual functions; otherwise, the derived class also becomes abstract.
class Derived: public Base {
public:
void display() {
cout << "Derived Class" << endl;
}
};
Since an abstract class cannot be instantiated, it is typically used through a base class pointer to achieve runtime polymorphism, in the same way as ordinary virtual functions, as follows.
Derived d;
Base *b;
b=&d;
b->display(); //calls display() of the derived class.
4. Virtual Destructors
When a base class pointer pointing to a derived class object is deleted, only the base class destructor is invoked by default, because the pointer type is that of the base class. However, in this case, the destructors of the base class and the derived class must be invoked to ensure the cleanup process is done for inherited and derived features. If we declare the base class destructor as virtual, then both the base class and derived class destructors are invoked. That's why we have to declare the base class destructor as virtual to ensure that both the base class and the derived class destructors are called.
class Base {
public:
virtual ~Base() // virtual destructor
{
cout << "Base destructor called" << endl;
}
};
class Derived: public Base {
public:
~Derived()
{
cout << "Derived destructor called" << endl;
}
};
5. Runtime Type Information (RTTI)
The runtime type information is one of the features of C++ that exhibit runtime polymorphic behavior. In C++, we can determine the type information of an object at runtime and safely downcast pointers. The operators dynamic_cast and typeid are used for runtime type information.
For example, if Animal is a polymorphic base class and Dog and Cat are derived classes of base class Animal, then
Animal *anmp;
Dog dg;
Cat ct;
anmp = &dg;
cout<< typeid(*anmp).name();
displays the information of the object pointed to by the anmp pointer. To use typeid, we require the <typeinfo> header to be included in our program.
The dynamic_cast operator safely converts a base class pointer to a derived class pointer at runtime, but returns NULL pointer if the cast is not valid.
For example
Cat *cpt;
anmp = &ct;
cpt=dynamic_cast<Cat*>(anmp); //successful
But
anmp = &dg;
cpt=dynamic_cast<Cat*>(anmp); //fail
The downcast is successful if anmp is holding the address of objects of class Cat, returns a NULL pointer if not.
Exercises
Create a class Person (members name and age) and two derived classes Employee (members employeeID and salary) and Student (members rollNo and program), inherited from class Person. Now create a class TeachingAssistant (members courseAssigned and weeklyHours) which is derived from two base classes Employee and Student. Show the use of the virtual base class.
Create a class UniversityMember containing the data members name and memberID. Derive classes Faculty and Administrator virtually from UniversityMember. Create another class Dean derived from both Faculty and Administrator. Display the order of constructor and destructor invocation and demonstrate that only one copy of UniversityMember is created.
Write a program to create a class Shape with functions to find the area of the shapes and display the names of the shapes. Create derived classes Circle, Rectangle, and Trapezoid, each having overriding functions area() and display(). Each derived class should have its own appropriate data members for the dimensions required to compute its area. Write a suitable program to illustrate virtual functions and virtual destructors. Include appropriate constructors and a virtual destructor in the base class.
Create an abstract class BankAccount containing data members accountNumber and holderName. Declare pure virtual functions deposit(), withdraw(), and displayBalance(). Derive classes SavingsAccount and CurrentAccount that implement these functions according to suitable rules such as minimum balance, withdrawal limits, etc. Include appropriate constructors and a virtual destructor in the base class. Create objects dynamically and delete them through base class pointers to illustrate both runtime polymorphism and virtual destructors.
Write a program to create a base class Payment with a virtual function processPayment(). Derive classes CashPayment, CardPayment, and OnlinePayment. Store all payment objects in an array of base class pointers. Use runtime polymorphism to process payments. Use typeid to display the payment type and dynamic_cast to identify only OnlinePayment objects and invoke an additional function verifyOTP().
Create an abstract class Vehicle containing common data members such as registrationNumber and brand. Declare pure virtual functions calculateFare() and displayDetails(), and include a virtual destructor in the base class. Create derived classes Bus, Car, and Bike, each implementing the pure virtual function.
Create the objects dynamically and store their addresses in an array of Vehicle pointers. Invoke the overridden functions through the base class pointers to demonstrate runtime polymorphism. Use the typeid operator to display the actual type of each object at runtime, and use dynamic_cast to safely identify a Bus object and invoke an additional function such as showSeatCapacity(). Finally, delete all objects through the base class pointers and demonstrate that both base and derived class destructors are called.