Virtual functions allow implementing polymorphism — the ability to handle objects of derived classes via pointers or references to the base class. The virtual keyword is used to declare a virtual function:
class Base { public: virtual void foo() { std::cout << "Base::foo"; } }; class Derived : public Base { public: void foo() override { std::cout << "Derived::foo"; } };
Calling foo() via a Base* pointer invokes the implementation in Derived if it points to a Derived object.
Abstract class — contains at least one pure virtual function:
class Interface { public: virtual void process() = 0; // pure virtual function };
Virtual inheritance addresses the diamond problem:
class A { }; class B : virtual public A { }; class C : virtual public A { }; class D : public B, public C { };
This ensures that there is a single instance of the base class A in the object D.
What is the difference between a virtual destructor and a regular one? Is it necessary to make a destructor virtual if the class is intended for inheritance?
Answer: A virtual destructor ensures that when deleting through a base type pointer, the derived class’s destructor will be called. This is important for properly freeing resources.
class Base { public: virtual ~Base() {} };
If the destructor is not virtual, then when delete BasePtr; is called, only the Base destructor will be called for the derived class object, and resources of inherited fields will not be released.
Story
In a large financial system, where different instruments had a common class interface, a virtual destructor was not declared. One of the descendants used a dynamic resource. When deleting an object through a pointer to the base type, a memory leak occurred, which was only discovered during industrial load testing.
Story
The team used multiple inheritance without applying virtual inheritance. Class D inherited A twice through intermediate classes. This led to state duplication and errors when accessing members of A. It was fixed only after an audit using static analysis tools.
Story
In a logging plugin development project, an abstract class was used, but its destructor was not made virtual. When deleting plugins via the interface pointer, obscure dependencies and bugs were observed, related to non-invoked destructors of descendants. The problem affected the resource pool and led to resource leaks.