A virtual function is a special type of member function that, when called, resolves to the most-derived version of the function for the actual type of the object being referenced or pointed to. This enables runtime polymorphism, allowing derived classes to override base class functions.
class Base
{
public:
virtual std::string_view getName() const { return "Base"; } // note addition of virtual keyword
};
class Derived: public Base
{
public:
virtual std::string_view getName() const { return "Derived"; }
};
int main()
{
Derived derived {};
Base& rBase{ derived };
std::cout << "rBase is a " << rBase.getName() << '\n'; // rbase is a Derived
}
override
Using the override
specifier ensures that the function is indeed overriding a base class function.
class A {
public:
virtual std::string_view getName() const { return "A"; }
};
class B : public A {
public:
std::string_view getName() const override { return "B"; } // Correct override
};
final
The final
specifier prevents further overriding of a virtual function or inheritance from a class.
class A {
public:
virtual std::string_view getName() const { return "A"; }
};
class B : public A {
public:
std::string_view getName() const override final { return "B"; } // Cannot be overridden further
};
class C : public B {
public:
std::string_view getName() const override { return "C"; } // Compile error: B::getName() is final
};
Whenever you are dealing with inheritance, you should make any explicit destructors virtual.
class Base {
public:
virtual ~Base() = default; // Virtual destructor
};
A pure virtual function is a virtual function that is declared by using the = 0
syntax. Classes with pure virtual functions become abstract base classes and cannot be instantiated.
class Animal {
public:
virtual std::string_view speak() const = 0; // pure virtual function
};
class Dog : public Animal {
public:
std::string_view speak() const override { return "Woof"; }
};
Pure virtual functions can still have a definition.
std::string_view Animal::speak() const
{
return "buzz";
}
An interface class is a class that has no member variables, and all of its functions are pure virtual.
class Interface {
public:
virtual void func1() = 0;
virtual void func2() = 0;
};
To share a base class in multiple inheritance, use virtual inheritance.
class PoweredDevice {};
class Scanner : virtual public PoweredDevice {};
class Printer : virtual public PoweredDevice {};
class Copier : public Scanner, public Printer {};
dynamic_cast
If a dynamic_cast
fails, the result of the conversion will be a null pointer.
Base* b = new Derived();
Derived* d = dynamic_cast<Derived*>(b); // use dynamic cast to convert Base pointer into Derived pointer
Note that there are several cases where downcasting using dynamic_cast
will not work:
static_cast
if (b->getClassID() == ClassID::derived)
{
Derived* d{ static_cast<Derived*>(b) }; // safe downcast
}
__vptr
, and thus each object of that class will be bigger by one pointer.friend std::ostream& operator<<(std::ostream& out, const Base& b)
{
// Delegate printing responsibility for printing to virtual member function print()
return b.print(out);
}
// We'll rely on member function print() to do the actual printing
// Because print() is a normal member function, it can be virtualized
virtual std::ostream& print(std::ostream& out) const
{
out << "Base";
return out;
}
Virtual functions enable runtime polymorphism, allowing derived classes to override base class functions. Understanding how to use virtual
, override
, final
, and pure virtual functions is crucial for designing robust and maintainable object-oriented systems.