A virtual function in C++ is a function that is defined in a base class and is meant to be overridden by derived classes. It is used to achieve runtime polymorphism, where the function call is resolved at runtime rather than compile-time. Virtual functions allow for dynamic dispatch, meaning that the correct function (the one corresponding to the actual object type, not the pointer type) is called at runtime.
Runtime Polymorphism: Virtual functions enable runtime polymorphism, where the method that is called is determined at runtime based on the type of the object being referred to by the pointer or reference.
Overriding: A virtual function in a base class can be overridden by a function in a derived class. When a virtual function is overridden, the derived class function is called instead of the base class function.
Virtual Keyword: The virtual keyword is used in the base class to declare a function as virtual. The keyword ensures that the derived class method (if overridden) is called when invoked through a base class pointer or reference.
Base Class Pointers and References: Virtual functions work when they are called through pointers or references to base class objects. If a function is called using a base class pointer or reference, C++ determines which version of the function to call based on the actual object type (i.e., the type of the object the pointer/reference is pointing to).
Destructor: If you have a base class with virtual functions, it is also important to declare the destructor as virtual to ensure proper cleanup of resources in derived class objects when they are deleted through base class pointers.
class Base {
public:
// Declare a virtual function in the base class
virtual void functionName();
};
class Derived : public Base {
public:
// Override the virtual function in the derived class
void functionName() override;
};
#include <iostream>
using namespace std;
class Base {
public:
// Virtual function in the base class
virtual void display() {
cout << "Base class display function" << endl;
}
};
class Derived : public Base {
public:
// Override the virtual function in the derived class
void display() override {
cout << "Derived class display function" << endl;
}
};
int main() {
Base *basePtr;
Derived derivedObj;
basePtr = &derivedObj;
// Call the virtual function through the base class pointer
basePtr->display(); // Output: Derived class display function
return 0;
}
Explanation:
Base has a virtual function display(), and Derived overrides it.basePtr is used to point to a Derived object.basePtr->display() is called, it invokes the overridden function in the derived class, showing the output from the Derived class, not the Base class.Virtual Functions and Derived Class:
override (optional but recommended) in the derived class ensures that the function properly overrides a virtual function from the base class, helping to prevent accidental mistakes.Virtual Destructor:
class Base {
public:
virtual ~Base() { // Virtual destructor
cout << "Base class destructor" << endl;
}
};
class Derived : public Base {
public:
~Derived() override { // Overridden destructor
cout << "Derived class destructor" << endl;
}
};
class Base {
public:
virtual void display() = 0; // Pure virtual function
};
class Derived : public Base {
public:
void display() override {
cout << "Derived class display function" << endl;
}
};
Explanation:
display() function is a pure virtual function (= 0), which means it must be overridden by any derived class.#include <iostream>
using namespace std;
class Base {
public:
Base() { cout << "Base class constructor" << endl; }
virtual ~Base() { cout << "Base class destructor" << endl; } // Virtual destructor
};
class Derived : public Base {
public:
Derived() { cout << "Derived class constructor" << endl; }
~Derived() override { cout << "Derived class destructor" << endl; }
};
int main() {
Base *basePtr = new Derived();
delete basePtr; // Proper destruction of derived class object
return 0;
}
Explanation:
Base class has a virtual destructor, ensuring that when the base pointer basePtr is used to delete a Derived object, both the Derived and Base destructors are called.Output:
Base class constructor
Derived class constructor
Derived class destructor
Base class destructor
#include <iostream>
using namespace std;
class Animal {
public:
virtual void sound() {
cout << "Animal makes a sound" << endl;
}
};
class Dog : public Animal {
public:
void sound() override {
cout << "Dog barks" << endl;
}
};
class Puppy : public Dog {
public:
void sound() override {
cout << "Puppy whines" << endl;
}
};
int main() {
Animal *animalPtr;
Dog dogObj;
Puppy puppyObj;
animalPtr = &dogObj;
animalPtr->sound(); // Output: Dog barks
animalPtr = &puppyObj;
animalPtr->sound(); // Output: Puppy whines
return 0;
}
Explanation:
Animal → Dog → Puppy.animalPtr is of type Animal*, the correct function is called based on the actual object type (Dog or Puppy).Output:
Dog barks
Puppy whines
Achieve Runtime Polymorphism: Virtual functions allow the correct function to be called based on the actual object type, enabling runtime polymorphism and making code more flexible.
Dynamic Dispatch: By using virtual functions, C++ determines which function to call at runtime, based on the actual object type pointed to by the base class pointer or reference.
Support for Abstract Classes: Virtual functions, especially pure virtual functions, support the creation of abstract classes that define common interfaces for derived classes.
Proper Cleanup with Virtual Destructors: When you have a base class pointer that points to a derived class object, using a virtual destructor ensures that both base and derived class destructors are called, avoiding memory leaks.
Virtual functions are a fundamental concept in C++ that enable runtime polymorphism, making it possible to call the correct function for an object even when accessed through a pointer or reference to a base class. By using the virtual keyword, derived classes can override base class functions, and virtual destructors ensure proper object destruction. Virtual functions play a key role in object-oriented programming, providing flexibility, extensibility, and maintainability.
Open this section to load past papers