Virtual functions are member functions declared in a base class and intended to be overridden in derived classes. The keyword virtual
is used to enable runtime polymorphism, allowing the program to dynamically determine which function implementation to call based on the actual type of the object rather than the static type of the reference or pointer.
When a base class declares a function as virtual
, derived classes can override this function to provide their specific implementation. This facilitates dynamic binding or late binding, where the function call is resolved at runtime.
Virtual functions enable key object-oriented programming features:
To declare a virtual function, use the virtual
keyword in the base class. It is usually specified in the function’s declaration, but can also be included in the definition.
class Base {
public:
virtual void show() {
std::cout << "Base class show function called." << std::endl;
}
};
class Derived : public Base {
public:
void show() override {
std::cout << "Derived class show function called." << std::endl;
}
};
In this example, the show
function in the Base
class is declared as virtual
, allowing it to be overridden in the Derived
class.
Here is an example demonstrating how virtual functions enable polymorphism:
#include <iostream>
using namespace std;
class Base {
public:
virtual void display() {
cout << "Display of Base class" << endl;
}
void show() {
cout << "Show of Base class" << endl;
}
};
class Derived : public Base {
public:
void display() override {
cout << "Display of Derived class" << endl;
}
void show() {
cout << "Show of Derived class" << endl;
}
};
int main() {
Base* basePtr;
Derived derivedObj;
basePtr = &derivedObj;
// Virtual function, binded at runtime
basePtr->display(); // Output: "Display of Derived class"
// Non-virtual function, binded at compile time
basePtr->show(); // Output: "Show of Base class"
return 0;
}
In this example:
display
function is virtual, so calling basePtr->display()
invokes the Derived
class implementation due to dynamic binding.show
function is not virtual, so calling basePtr->show()
invokes the Base
class implementation due to static binding (compile-time).To override a virtual function in a derived class, define a function with the same signature as the base class’s virtual function. The override
keyword can be used for better readability and to ensure that the function is correctly overriding a virtual function.
class Derived : public Base {
public:
void display() override { // 'override' ensures this function overrides a base virtual function
cout << "Display of Derived class" << endl;
}
};
If the function signature does not match any virtual function in the base class, the compiler will generate an error when using override
.
A pure virtual function is a virtual function with no implementation in the base class, making the class abstract. Such a class cannot be instantiated and serves as a base for derived classes.
Syntax for a pure virtual function:
class Base {
public:
virtual void display() = 0; // Pure virtual function
};
Any class that derives from Base
must provide an implementation for the display
function. If it doesn’t, that derived class will also be considered abstract.
Virtual Destructors: If a class has virtual functions, its destructor should also be declared as virtual to ensure that derived class destructors are called when deleting an object through a base class pointer.
class Base {
public:
virtual ~Base() {
cout << "Base destructor called" << endl;
}
};
Access Specifiers: Virtual functions can have any access specifier (public
, protected
, or private
). However, they are usually declared as public.
Constructors Cannot Be Virtual: Constructors cannot be declared as virtual in C++, as objects are not fully formed during construction.
Static Member Functions: Virtual functions cannot be static because static functions are not tied to a specific object instance.
Multiple Levels of Inheritance: If a class hierarchy has multiple levels, a derived class can still override a base class’s virtual function.
class GrandChild : public Derived {
public:
void display() override {
cout << "Display of GrandChild class" << endl;
}
};
While virtual functions provide powerful polymorphism, they come with some overhead:
inline
, because their address is determined at runtime.Here is an example illustrating the importance of virtual destructors:
#include <iostream>
using namespace std;
class Base {
public:
virtual ~Base() {
cout << "Base destructor called" << endl;
}
};
class Derived : public Base {
public:
~Derived() {
cout << "Derived destructor called" << endl;
}
};
int main() {
Base* obj = new Derived();
delete obj; // Both Derived and Base destructors are called
return 0;
}
In this example, if the destructor in the Base
class were not virtual, only the Base
destructor would be called, leading to potential resource leaks.