Contents
Virtual Function
Virtual function in C++
A virtual function (also referred to as virtual methods) is a member function declared in a base class that can be overridden in a derived class. When a derived class object is referred to using a pointer or reference of the base class, the virtual function allows the derived class’s implementation to be executed.
Virtual functions ensure the correct function is called based on the object type, regardless of the reference (or pointer) type used. They play a key role in enabling runtime polymorphism. Functions are declared using the virtual
keyword in the base class, and their call resolution is performed at runtime.
Key Rules for Virtual Functions
- Virtual functions cannot be static.
- They can be friends of another class.
- To achieve runtime polymorphism, virtual functions should be accessed using a pointer or reference to a base class.
- The function prototypes must be the same in the base and derived classes.
- They are always defined in the base class and overridden in the derived class. If the derived class does not override the virtual function, the base class version will be used.
- A class can have a virtual destructor but not a virtual constructor.
Early Binding vs. Late Binding
Virtual functions demonstrate late binding or runtime polymorphism, where the function call is resolved during runtime. In contrast, non-virtual functions exhibit early binding or compile-time binding, where the function call is resolved during compilation.
Example: Runtime Behavior of Virtual Functions
#include
using namespace std;
class Base {
public:
virtual void display() { cout << "Base class display\n"; }
void show() { cout << "Base class show\n"; }
};
class Derived : public Base {
public:
void display() { cout << "Derived class display\n"; }
void show() { cout << "Derived class show\n"; }
};
int main() {
Base* basePtr;
Derived derivedObj;
basePtr = &derivedObj;
// Virtual function, resolved at runtime
basePtr->display();
// Non-virtual function, resolved at compile time
basePtr->show();
return 0;
}
Output:
Derived class display
Base class show
Explanation: In this example, basePtr
is a pointer of type Base
, but it points to an object of type Derived
. The virtual function display()
is resolved at runtime, so the Derived
class version is called. On the other hand, the non-virtual function show()
is resolved at compile-time, and the Base
class version is executed.
Working of Virtual Functions (VTABLE and VPTR)
When a class contains virtual functions, the compiler takes the following actions:
1. For every class that contains a virtual function, the compiler creates a vtable (virtual table), which is a static array of function pointers. Each cell of the vtable stores the address of a virtual function in that class.
2. For every object of a class containing virtual functions, a vptr (virtual pointer) is added as a hidden data member. This pointer refers to the vtable of the class to which the object belongs.
This mechanism allows the correct virtual function to be called at runtime based on the actual object type.
Example: Working of Virtual Functions
#include
using namespace std;
class Base {
public:
void func1() { cout << "Base - func1\n"; }
virtual void func2() { cout << "Base - func2\n"; }
virtual void func3() { cout << "Base - func3\n"; }
virtual void func4() { cout << "Base - func4\n"; }
};
class Derived : public Base {
public:
void func1() { cout << "Derived - func1\n"; }
void func2() { cout << "Derived - func2\n"; }
void func4(int x) { cout << "Derived - func4 with parameter\n"; }
};
int main() {
Base* basePtr;
Derived derivedObj;
basePtr = &derivedObj;
// Early binding (compile-time)
basePtr->func1();
// Late binding (runtime)
basePtr->func2();
// Late binding (runtime)
basePtr->func3();
// Late binding (runtime)
basePtr->func4();
// Illegal: Early binding but function does not exist in base class
// basePtr->func4(5);
return 0;
}
Output:
Base - func1
Derived - func2
Base - func3
Base - func4
Explanation:
func1()
is a non-virtual function, so it is resolved at compile-time, and theBase
class version is called.func2()
is a virtual function, so theDerived
class version is called at runtime.func3()
andfunc4()
are virtual functions, and sincefunc3()
is not overridden in the derived class, theBase
class version is called. Similarly,func4()
is also resolved to theBase
class version.
a < b : 0
a > b : 1
a <= b: 0
a >= b: 1
a == b: 0
a != b : 1
A virtual function in a base class can be overridden by a derived class. When a derived class object is referenced or pointed to by a base class reference or pointer, calling a virtual function will invoke the version defined in the derived class.
In C++, once a function is marked as virtual in a base class, it remains virtual throughout all derived classes. Therefore, you don’t need to explicitly declare it as virtual
again in derived classes that override the function.
For instance, in the example below, even though the keyword virtual
is only used in the base class A
, the function fun()
in B
and C
is virtual. This is demonstrated by the fact that the program prints “C::fun() called”, as B::fun()
inherits the virtual property.
Example
#include
using namespace std;
class Animal {
public:
virtual void sound() { cout << "\n Animal sound"; }
};
class Dog : public Animal {
public:
void sound() { cout << "\n Dog barks"; }
};
class Puppy : public Dog {
public:
void sound() { cout << "\n Puppy squeaks"; }
};
int main() {
// Create an object of class Puppy
Puppy p;
// Pointer of class Dog pointing to object of class Puppy
Dog* dogPtr = &p;
// This line prints "Puppy squeaks" due to the virtual function mechanism
dogPtr->sound();
return 0;
}
Output:
Puppy squeaks
Virtual Functions in Derived Classes
A virtual function is a member function in a base class that can be overridden in a derived class. When a pointer or reference to the base class is used to refer to an object of the derived class, the derived class’s version of the virtual function is executed.
Once a function is marked as virtual in a base class in C++, it remains virtual throughout all derived classes. You don’t need to explicitly declare it as virtual
in the derived classes when overriding it.
For instance, in the following program, even though the keyword virtual
is used only in class A
, the function fun()
in B
and C
behaves as virtual. As a result, the program outputs “C::display() called”, since the virtual function mechanism ensures that B::fun()
is automatically virtual.
Example
#include
using namespace std;
class Shape {
public:
virtual void draw() { cout << "\n Drawing Shape"; }
};
class Circle : public Shape {
public:
void draw() { cout << "\n Drawing Circle"; }
};
class FilledCircle : public Circle {
public:
void draw() { cout << "\n Drawing Filled Circle"; }
};
int main() {
// Object of class FilledCircle
FilledCircle filledCircle;
// Pointer of class Circle pointing to object of FilledCircle
Circle* circlePtr = &filledCircle;
// This line prints "Drawing Filled Circle"
circlePtr->draw();
return 0;
}
Output:
Drawing Filled Circle
Default Arguments and Virtual Function
Default arguments allow you to provide initial values in function declarations that will be used if no arguments are passed during a function call. If arguments are provided, the default values are overridden. Virtual functions, on the other hand, enable runtime polymorphism by allowing a derived class to override a function in the base class.
Combining default arguments with virtual functions can lead to interesting behavior. Let’s explore how they work together.
Example:
#include
using namespace std;
// Base class declaration
class Animal {
public:
// Virtual function with a default argument
virtual void sound(int volume = 5) {
cout << "Animal sound at volume: " << volume << endl;
}
};
// Derived class overriding the virtual function
class Dog : public Animal {
public:
// Virtual function without a default argument
virtual void sound(int volume) {
cout << "Dog barks at volume: " << volume << endl;
}
};
// Main function
int main() {
Dog dog;
// Pointer to base class points to derived class object
Animal* animalPtr = &dog;
// Calls derived class function, default argument from base class
animalPtr->sound();
return 0;
}
Output:
Dog barks at volume: 5
Virtual Destructor
Deleting a derived class object using a pointer of base class type that does not have a virtual destructor leads to undefined behavior. This is because when deleting an object through a base class pointer, only the base class destructor will be invoked, leaving the derived class destructor uncalled. To resolve this issue, the base class should be defined with a virtual destructor.
Consider the following example, where the lack of a virtual destructor in the base class causes undefined behavior:
// Example with non-virtual destructor, causing undefined behavior
#include
class Base {
public:
Base() {
std::cout << "Base class constructor\n";
}
~Base() {
std::cout << "Base class destructor\n";
}
};
class Derived : public Base {
public:
Derived() {
std::cout << "Derived class constructor\n";
}
~Derived() {
std::cout << "Derived class destructor\n";
}
};
int main() {
Derived* d = new Derived();
Base* b = d;
delete b; // Undefined behavior: Derived class destructor not called
return 0;
}
Output:
Base class constructor
Derived class constructor
Base class destructor
As we can see, only the base class destructor is called, and the derived class destructor is skipped, leading to potential memory leaks or other issues.
To ensure both base and derived destructors are properly invoked, we must make the base class destructor virtual, as shown in the following example:
// Example with virtual destructor
#include
class Base {
public:
Base() {
std::cout << "Base class constructor\n";
}
virtual ~Base() {
std::cout << "Base class destructor\n";
}
};
class Derived : public Base {
public:
Derived() {
std::cout << "Derived class constructor\n";
}
~Derived() {
std::cout << "Derived class destructor\n";
}
};
int main() {
Derived* d = new Derived();
Base* b = d;
delete b; // Both base and derived destructors are called
return 0;
}
Output:
Base class constructor
Derived class constructor
Derived class destructor
Base class destructor
Virtual Constructor
In C++, we cannot make a constructor virtual. The reason is that C++ is a statically typed language, and a virtual constructor contradicts this because the compiler needs to determine the exact type at compile time to allocate the right amount of memory and initialize the object properly.
Attempting to declare a constructor as virtual will result in a compiler error. In fact, apart from the inline
keyword, no other keyword is permitted in constructor declarations.
Problem: Tightly Coupled Object Creation
In real-world scenarios, you may want to instantiate objects from a class hierarchy based on runtime conditions or user input. However, object creation in C++ is tightly coupled to specific class types, meaning that whenever a new class is added to a hierarchy, code changes and recompilation are required.
For instance, consider a situation where the User
class always creates an object of type Derived1
. If at some point you want to create an object of Derived2
, you would need to modify the User
class to instantiate Derived2
instead, which forces recompilation—a design flaw.
Example of Tight Coupling
Here’s an example of how object creation can lead to tight coupling:
// C++ program showing tight coupling issue in object creation
#include
using namespace std;
class Base {
public:
Base() {}
virtual ~Base() {}
virtual void DisplayAction() = 0;
};
class Derived1 : public Base {
public:
Derived1() {
cout << "Derived1 created\n";
}
~Derived1() {
cout << "Derived1 destroyed\n";
}
void DisplayAction() {
cout << "Action from Derived1\n";
}
};
class Derived2 : public Base {
public:
Derived2() {
cout << "Derived2 created\n";
}
~Derived2() {
cout << "Derived2 destroyed\n";
}
void DisplayAction() {
cout << "Action from Derived2\n";
}
};
class User {
public:
User() : pBase(nullptr) {
pBase = new Derived1(); // Always creating Derived1
// But what if Derived2 is needed?
}
~User() {
delete pBase;
}
void Action() {
pBase->DisplayAction();
}
private:
Base *pBase;
};
int main() {
User *user = new User();
user->Action();
delete user;
}
Output:
Derived1 created
Action from Derived1
Derived1 destroyed
In this example, the User
class is tightly coupled to Derived1
. If a consumer requires the functionality of Derived2
, the User
class must be modified and recompiled to create Derived2
. This is inflexible and leads to poor design.
Methods for Decoupling
Here are common methods to decouple tightly coupled classes:
1. Modifying with an If-Else Ladder (Not Extensible) : One way to address this issue is by adding an if-else ladder to select the desired derived class based on input. However, this solution still suffers from the same problem.
// C++ program showing how to use if-else for object creation
#include
using namespace std;
class Base {
public:
Base() {}
virtual ~Base() {}
virtual void DisplayAction() = 0;
};
class Derived1 : public Base {
public:
Derived1() {
cout << "Derived1 created\n";
}
~Derived1() {
cout << "Derived1 destroyed\n";
}
void DisplayAction() {
cout << "Action from Derived1\n";
}
};
class Derived2 : public Base {
public:
Derived2() {
cout << "Derived2 created\n";
}
~Derived2() {
cout << "Derived2 destroyed\n";
}
void DisplayAction() {
cout << "Action from Derived2\n";
}
};
class User {
public:
User() : pBase(nullptr) {
int input;
cout << "Enter ID (1 or 2): ";
cin >> input;
if (input == 1)
pBase = new Derived1();
else
pBase = new Derived2();
}
~User() {
delete pBase;
}
void Action() {
pBase->DisplayAction();
}
private:
Base *pBase;
};
int main() {
User *user = new User();
user->Action();
delete user;
}
Output:
Enter ID (1 or 2): 2
Derived2 created
Action from Derived2
Derived2 destroyed
This approach is still not extensible. If another derived class, Derived3
, is added to the hierarchy, the User
class must be updated and recompiled.
2. The Factory Method (Best Solution) : A better solution is to delegate the responsibility of object creation to the class hierarchy itself or a static function, thereby decoupling the User
class from the derived classes. This approach is known as the Factory Method.
// C++ program using Factory Method for object creation
#include
using namespace std;
class Base {
public:
static Base* Create(int id);
Base() {}
virtual ~Base() {}
virtual void DisplayAction() = 0;
};
class Derived1 : public Base {
public:
Derived1() {
cout << "Derived1 created\n";
}
~Derived1() {
cout << "Derived1 destroyed\n";
}
void DisplayAction() {
cout << "Action from Derived1\n";
}
};
class Derived2 : public Base {
public:
Derived2() {
cout << "Derived2 created\n";
}
~Derived2() {
cout << "Derived2 destroyed\n";
}
void DisplayAction() {
cout << "Action from Derived2\n";
}
};
Base* Base::Create(int id) {
if (id == 1) return new Derived1();
else return new Derived2();
}
class User {
public:
User() : pBase(nullptr) {
int input;
cout << "Enter ID (1 or 2): ";
cin >> input;
pBase = Base::Create(input);
}
~User() {
delete pBase;
}
void Action() {
pBase->DisplayAction();
}
private:
Base *pBase;
};
int main() {
User *user = new User();
user->Action();
delete user;
}
Output:
Enter ID (1 or 2): 1
Derived1 created
Action from Derived1
Derived1 destroyed
Virtual Copy Constructor
In the virtual constructor idiom, we have seen how to construct an object whose type is determined at runtime. But is it possible to copy an object without knowing its exact type at compile time? The virtual copy constructor addresses this problem.
Sometimes, we need to create a new object based on the state of an existing object. Typically, this is done using the copy constructor, which initializes the new object with the state of the existing one. The compiler invokes the copy constructor when an object is instantiated from another. However, the compiler must know the exact type of the object to invoke the appropriate copy constructor.
Example Without Virtual Copy Constructor:
#include
using namespace std;
class Base {
// Base class
};
class Derived : public Base {
public:
Derived() {
cout << "Derived created" << endl;
}
// Copy constructor
Derived(const Derived &rhs) {
cout << "Derived created by deep copy" << endl;
}
~Derived() {
cout << "Derived destroyed" << endl;
}
};
int main() {
Derived d1;
Derived d2 = d1; // Compiler calls the copy constructor
// How to copy a Derived object through a Base pointer?
return 0;
}
Problem: Copying at Runtime Without Knowing Type
The virtual constructor creates objects of a derived class based on runtime input. When we want to copy one of these objects, we can’t use the regular copy constructor because the exact type of the object isn’t known at compile time. Instead, we need a virtual function that can duplicate the object during runtime, based on its actual type.
Consider a drawing application where users can select an object and duplicate it. The type of object (circle, square, etc.) isn’t known until runtime, so the virtual copy constructor (or a clone function) is needed to correctly copy and paste the object.
Updated Example Using Virtual Copy Constructor:
Prefix Increment: Increases the operand’s value before it is used in an expression.
#include
using namespace std;
// Base class
class Base {
public:
Base() {}
// Virtual destructor to ensure correct cleanup
virtual ~Base() {}
// Pure virtual function to change object attributes
virtual void ModifyAttributes() = 0;
// Virtual constructor
static Base* Create(int id);
// Virtual copy constructor
virtual Base* Clone() = 0;
};
// Derived1 class
class Derived1 : public Base {
public:
Derived1() {
cout << "Derived1 created" << endl;
}
// Copy constructor
Derived1(const Derived1& rhs) {
cout << "Derived1 created by deep copy" << endl;
}
~Derived1() {
cout << "~Derived1 destroyed" << endl;
}
void ModifyAttributes() {
cout << "Derived1 attributes modified" << endl;
}
// Clone method (virtual copy constructor)
Base* Clone() {
return new Derived1(*this);
}
};
// Derived2 class
class Derived2 : public Base {
public:
Derived2() {
cout << "Derived2 created" << endl;
}
// Copy constructor
Derived2(const Derived2& rhs) {
cout << "Derived2 created by deep copy" << endl;
}
~Derived2() {
cout << "~Derived2 destroyed" << endl;
}
void ModifyAttributes() {
cout << "Derived2 attributes modified" << endl;
}
// Clone method (virtual copy constructor)
Base* Clone() {
return new Derived2(*this);
}
};
// Derived3 class
class Derived3 : public Base {
public:
Derived3() {
cout << "Derived3 created" << endl;
}
// Copy constructor
Derived3(const Derived3& rhs) {
cout << "Derived3 created by deep copy" << endl;
}
~Derived3() {
cout << "~Derived3 destroyed" << endl;
}
void ModifyAttributes() {
cout << "Derived3 attributes modified" << endl;
}
// Clone method (virtual copy constructor)
Base* Clone() {
return new Derived3(*this);
}
};
// Factory method to create objects at runtime
Base* Base::Create(int id) {
if (id == 1)
return new Derived1;
else if (id == 2)
return new Derived2;
else
return new Derived3;
}
// Utility class to use Base objects
class User {
public:
User() : pBase(nullptr) {
int id;
cout << "Enter ID (1, 2, or 3): ";
cin >> id;
// Create object at runtime using factory method
pBase = Base::Create(id);
}
~User() {
if (pBase) {
delete pBase;
pBase = nullptr;
}
}
void PerformAction() {
// Clone the current object (runtime copy)
Base* clonedObj = pBase->Clone();
// Modify the cloned object's attributes
clonedObj->ModifyAttributes();
// Cleanup the cloned object
delete clonedObj;
}
private:
Base* pBase;
};
// Client code
int main() {
User* user = new User();
user->PerformAction();
delete user;
return 0;
}
Pure Virtual Functions and Abstract Classes
Sometimes, we may not provide the complete implementation of all functions in a base class because the specifics are not known. Such a class is called an abstract class. For example, consider a base class called Shape
. We cannot define how draw()
works in the base class, but every derived class (like Circle
, Rectangle
, etc.) must provide an implementation for draw()
. Similarly, an Animal
class might have a move()
function that all animals need to implement, but the way they move may differ. We cannot create objects of abstract classes.
A pure virtual function (also known as an abstract function) in C++ is a virtual function that has no definition in the base class, and we force derived classes to provide an implementation. If a derived class does not override this function, the derived class will also become an abstract class. A pure virtual function is declared by assigning it a value of 0 in its declaration.
Example of a Pure Virtual Function:
// Abstract class
class AbstractClass {
public:
// Pure Virtual Function
virtual void display() = 0;
};
Complete Example: A pure virtual function must be implemented by any class that is derived from an abstract class.
#include
using namespace std;
class Base {
// Private data member
int x;
public:
// Pure virtual function
virtual void show() = 0;
// Getter function to access x
int getX() { return x; }
};
// Derived class implementing the pure virtual function
class Derived : public Base {
// Private data member
int y;
public:
// Implementation of the pure virtual function
void show() { cout << "show() function called" << endl; }
};
int main() {
// Creating an object of the Derived class
Derived d;
// Calling the show() function
d.show();
return 0;
}
Output:
show() function called
Key Points
1. Abstract Classes: A class becomes abstract if it contains at least one pure virtual function.
Example: In the code below, the class Example
is abstract because it has a pure virtual function display()
.
#include
using namespace std;
class Example {
int x;
public:
// Pure virtual function
virtual void display() = 0;
// Getter function for x
int getX() { return x; }
};
int main() {
// Error: Cannot instantiate an abstract class
Example ex;
return 0;
}
Output:
Compiler Error: Cannot instantiate an abstract class
The compiler will throw an error because Example
is an abstract class, and we cannot create an object of it.
2. Pointers and References to Abstract Classes: Although you cannot instantiate an abstract class, you can create pointers and references to it. This allows polymorphism, where a pointer to an abstract class can point to any derived class object.
Example:
#include
using namespace std;
class Base {
public:
// Pure virtual function
virtual void show() = 0;
};
class Derived : public Base {
public:
// Implementation of the pure virtual function
void show() { cout << "Derived class implementation" << endl; }
};
int main() {
// Pointer to Base class pointing to Derived object
Base* ptr = new Derived();
// Calling the show() function via the pointer
ptr->show();
delete ptr;
return 0;
}
Output:
Derived class implementation
3. Derived Classes Become Abstract: If a derived class does not override the pure virtual function, it will also be treated as an abstract class, meaning you cannot instantiate it.
Example:
#include
using namespace std;
class Base {
public:
// Pure virtual function
virtual void show() = 0;
};
class Derived : public Base {
// No override for the pure virtual function
};
int main() {
// Error: Cannot create an object of an abstract class
Derived d;
return 0;
}
Output:
Compiler Error: Derived is an abstract class because 'show()' is not implemented
Pure Virtual Destructor in C++
Can a Destructor Be Pure Virtual in C++?
Yes, in C++, a destructor can be declared as pure virtual. Pure virtual destructors are legal and serve an essential role, especially in abstract base classes. However, if a class contains a pure virtual destructor, you must provide a definition for the destructor. This requirement arises from the fact that destructors are not overridden like other functions but are always called in reverse order of class inheritance. Without a definition for the pure virtual destructor, there will be no valid function body to call during object destruction.
Why Must a Pure Virtual Destructor Have a Body?
The reason behind this requirement is that destructors are always invoked in reverse order during the destruction of objects. When a derived class object is deleted, its destructor is called first, followed by the base class destructor. Since the base class destructor must still execute, a function body is needed, even for pure virtual destructors. This ensures that resources allocated by the base class are cleaned up properly when an object is destroyed.
Example of a Pure Virtual Destructor
#include
using namespace std;
// Base class with a pure virtual destructor
class Base {
public:
virtual ~Base() = 0; // Pure virtual destructor
};
// Definition of the pure virtual destructor
Base::~Base() {
cout << "Base class destructor called\n";
}
// Derived class implementing its destructor
class Derived : public Base {
public:
~Derived() { cout << "Derived class destructor called\n"; }
};
// Driver code
int main() {
Base* ptr = new Derived();
delete ptr;
return 0;
}
Output:
Derived class destructor called
Base class destructor called
In the example above, both the derived class and base class destructors are called in reverse order, ensuring proper cleanup.
How It Works
This works because destructors are always called in reverse order, starting from the most derived class back to the base class. When the delete
operator is called on a pointer to a derived object, the derived class destructor is executed first, followed by the base class destructor. This is only possible because we provided a definition for the pure virtual destructor in the base class.
Important Points
Example: Demonstrating Abstract Class with Pure Virtual Destructor
#include
using namespace std;
class AbstractClass {
public:
// Pure virtual destructor
virtual ~AbstractClass() = 0;
};
// Defining the pure virtual destructor
AbstractClass::~AbstractClass() {
cout << "AbstractClass destructor called\n";
}
// Attempting to create an object of AbstractClass will result in an error
int main() {
// AbstractClass obj; // This will cause a compiler error
return 0;
}
Error: Cannot instantiate abstract class ‘AbstractClass’ because its destructor is pure virtual.
This error occurs because AbstractClass
is abstract, meaning it contains a pure virtual function, making it impossible to instantiate. The error message confirms that the pure virtual destructor makes the class abstract.
Can Static Functions Be Virtual in C++?
In C++, static member functions of a class cannot be virtual. Virtual functions are invoked through a pointer or reference to an object of the class, allowing polymorphic behavior. However, static member functions are associated with the class itself, rather than any specific instance of the class. This distinction prevents them from being virtual, as there is no object to refer to for invoking them virtually.
Example Demonstrating Static Functions Cannot Be Virtual
Attempting to declare a static member function as virtual will result in a compilation error:
// C++ program demonstrating that static member functions
// cannot be virtual
#include
class Example {
public:
virtual static void display() {}
};
int main() {
Example e;
return 0;
}
Output:
error: member ‘display’ cannot be declared both virtual and static
The error occurs because C++ does not allow the combination of virtual
and static
for member functions, as static functions are tied to the class, not to specific instances.
Static Member Functions Cannot Be const
or volatile
Static member functions are also not allowed to be declared const
or volatile
. Since const
and volatile
qualifiers are applied to member functions to indicate whether they can modify the state of the object or respond to volatility, static functions—being associated with the class rather than instances—cannot access instance-specific data members, making these qualifiers meaningless.
Example Demonstrating Static Member Functions Cannot Be const
Attempting to declare a static member function as const
will also fail at compile time:
// C++ program demonstrating that static member functions
// cannot be const
#include
class Example {
public:
static void display() const {}
};
int main() {
Example e;
return 0;
}
Example:
error: static member function ‘static void Example::display()’ cannot have cv-qualifier
Run-time Type Information (RTTI) in C++
In this example, we demonstrate how dynamic_cast
fails when the base class does not contain a virtual function. Since RTTI (Run-Time Type Information) requires at least one virtual function in the base class for dynamic_cast
to function, this example will lead to a runtime failure.
#include
using namespace std;
// Base class without a virtual function
class Base {};
// Derived class inheriting from Base
class Derived : public Base {};
int main() {
Base* basePtr = new Derived; // Base class pointer pointing to a Derived object
// Attempting to downcast using dynamic_cast
Derived* derivedPtr = dynamic_cast(basePtr);
if (derivedPtr != nullptr)
cout << "Cast successful" << endl;
else
cout << "Cannot cast Base* to Derived*" << endl;
return 0;
}
Expected Output:
Cannot cast Base* to Derived*
Here, the dynamic_cast
fails because there is no virtual function in the base class Base
, which makes RTTI unavailable. As a result, the cast does not succeed.
Adding a Virtual Function to Enable RTTI
By adding a virtual function to the base class, RTTI is enabled, allowing dynamic_cast
to perform the necessary checks at runtime and making the cast succeed.
Example With a Virtual Function in the Base Class
#include
using namespace std;
// Base class with a virtual function
class Base {
public:
virtual void exampleFunction() {} // Virtual function to enable RTTI
};
// Derived class inheriting from Base
class Derived : public Base {};
int main() {
Base* basePtr = new Derived; // Base class pointer pointing to a Derived object
// Attempting to downcast using dynamic_cast
Derived* derivedPtr = dynamic_cast(basePtr);
if (derivedPtr != nullptr)
cout << "Cast successful" << endl;
else
cout << "Cannot cast Base* to Derived*" << endl;
return 0;
}
Expected Output:
Cast successful