Virtual Destructor & Constructor Philosophy in C++
1️⃣ The Situation
You have a base class with virtual functions.
You allocate a derived object using a base pointer:
class Base {
public:
virtual void func() { }
~Base() { cout << "Base dtor\n"; } // ❌ Non-virtual
};
class Derived : public Base {
public:
~Derived() { cout << "Derived dtor\n"; }
};
int main() {
Base* b = new Derived();
delete b; // ❌ Problem
}
What happens?
Only
Base::~Base()is called.Derived::~Derived()is skipped → Derived resources leak.Undefined behavior can result if Derived allocated memory, opened files, sockets, etc.
2️⃣ Why doesn’t the compiler make the destructor virtual automatically?
This is a design decision in C++ philosophy:
Virtual destructor = extra cost (vtable entry, runtime dispatch overhead, larger object size due to vptr).
Not every class with virtual functions is meant to be used polymorphically through a base pointer.
Example:
struct Logger { virtual void log() = 0; // interface-like // But maybe never deleted polymorphically };Automatically making every destructor virtual would impose cost even when not needed.
👉 C++ Philosophy = "Don’t pay for what you don’t use."
The programmer must explicitly declare the destructor virtual if polymorphic deletion is intended.
3️⃣ What does the compiler do then?
The compiler will not silently upgrade the destructor to virtual, even if the class already has other virtual functions.
Instead, many compilers will issue a warning if:
The base class has virtual functions but a non-virtual destructor.
Example warning (GCC/Clang):
warning: ‘Base’ has virtual functions but non-virtual destructor
👉 Ignoring this warning leads to undefined behavior when deleting derived objects via a base pointer.
4️⃣ Why is it undefined behavior?
When you do
delete b;wherebpoints to aDerivedobject:The compiler checks the static type (
Base*).Without a virtual destructor, only
Base::~Base()is invoked.The derived portion is left undestroyed → memory leak, dangling resources.
The C++ standard calls this undefined behavior, meaning anything can happen (leak, crash, silent corruption).
5️⃣ Correct Approach
Always make base class destructors virtual if you expect polymorphic use:
class Base {
public:
virtual void func() {}
virtual ~Base() { cout << "Base dtor\n"; } // ✅ Virtual
};
class Derived : public Base {
public:
~Derived() { cout << "Derived dtor\n"; }
};
int main() {
Base* b = new Derived();
delete b; // ✅ Calls Derived::~Derived() then Base::~Base()
}
Output:
Derived dtor
Base dtor
6️⃣ C++ Philosophy Recap
C++ never assumes intent — you must explicitly declare your design.
The compiler could technically make the destructor virtual if any virtual function exists.
But this would violate the “zero-cost abstraction” principle:
You pay only for what you ask for.
If you don’t need polymorphic deletion, no extra overhead is added.
7️⃣ Interview Takeaways
Destructor not virtual + base pointer delete derived object → UB (undefined behavior).
Compiler does not auto-make destructor virtual, even if virtual functions exist.
Why? Because of performance cost and C++ philosophy: "Don’t pay for what you don’t use."
Compiler warning ≠ error → ignoring leads to UB.
Rule of thumb: Always declare destructors virtual in base classes that are meant to be inherited and deleted polymorphically.
📝 Virtual Constructors in C++
✅ 1. Direct Answer
No, constructors cannot be virtual in C++.
And they are not necessary like virtual destructors.
🔎 2. Why constructors cannot be virtual?
Virtual functions need a vptr (object already constructed)
Virtual dispatch works via the vptr, which points to the vtable.
But the vptr is set up inside the constructor when the object is being built.
Before the constructor runs, the object does not even exist → no vptr yet.
So it’s impossible to call a constructor virtually.
Infinite loop problem
If constructors were virtual, the program would need to decide which constructor to call based on runtime type.
But runtime type cannot exist before construction.
That’s like asking “which house type am I building?” before even starting to lay the foundation.
Result: logical paradox → would lead to infinite recursion / impossible semantics.
Object creation is static
The type of object you want to create must be known at compile time.
Unlike destruction (which can be deferred until runtime), construction is the very definition of deciding the object type.
If you don’t know the type → you can’t allocate correct memory size or set up vtable.
🔧 Example
class Base {
public:
virtual void foo() {}
// virtual Base(); ❌ Error: constructors cannot be declared virtual
};
class Derived : public Base {};
Trying to declare a virtual constructor gives:
error: constructors cannot be declared ‘virtual’
📝 Why would I want polymorphic creation?
✅ 1. The Problem Setup
Imagine you are writing a program that needs to work with a base pointer, but at runtime, you don’t know which derived class you’ll actually need.
Example (without polymorphic creation):
Base* b = new Derived1(); // Hard-coded
Here you manually decide which derived class you want to create.
But what if you only know the type at runtime (e.g., from a config file, network request, user input)?
✅ 2. Why can’t constructor solve this?
Because constructors are not virtual, you can’t just write:
Base* b = new Base(); // ❌ Wrong, we want Derived1 or Derived2
And you can’t call something like Base::Base() polymorphically — because construction isn’t polymorphic.
So the need arises:
👉 “How do I create derived objects when I only have the base class interface?”
✅ 3. Solution: “Virtual Constructor Idioms”
Since C++ won’t give you a virtual constructor, you simulate one with design patterns.
🔹 Factory Method
class Base {
public:
virtual void show() = 0;
virtual ~Base() {}
// Static factory method
static Base* create(const string& type);
};
class Derived1 : public Base {
public:
void show() override { cout << "Derived1\n"; }
};
class Derived2 : public Base {
public:
void show() override { cout << "Derived2\n"; }
};
// "Virtual constructor" substitute
Base* Base::create(const string& type) {
if (type == "d1") return new Derived1();
if (type == "d2") return new Derived2();
return nullptr;
}
int main() {
Base* b = Base::create("d2"); // runtime choice
b->show(); // Output: Derived2
delete b;
}
👉 Here, Base::create() acts like a virtual constructor.
It decides which derived object to create at runtime.
🔹 Clone Idiom (Prototype Pattern)
Suppose you already have one object, and you want to create a copy of the same actual derived type without knowing its type explicitly:
class Base {
public:
virtual Base* clone() const = 0; // "Virtual constructor"
virtual ~Base() {}
};
class Derived : public Base {
public:
Derived* clone() const override { return new Derived(*this); }
};
int main() {
Base* d1 = new Derived();
Base* d2 = d1->clone(); // Makes a Derived copy without knowing type
delete d1;
delete d2;
}
👉 Here, clone() is effectively a constructor call, but virtual.
✅ 4. Real-World Need for Polymorphic Creation
Serialization/Deserialization: Reading objects from a file or network. You only know at runtime which derived type is coming in.
GUI Widgets: You call
Widget::create("Button")orWidget::create("Label")→ it returns the right derived widget.Game Engines: You might say “createEnemy(type=‘Orc’)” or “createEnemy(type=‘Dragon’)” → engine uses factory to decide which subclass to instantiate.
Without this pattern, you’d need to hard-code new Derived1() everywhere, which kills flexibility.
✅ 5. Contrast with Destructor
Destructor must be virtual because you often destroy objects polymorphically (
delete basePtr;).Constructor cannot be virtual, but sometimes you need polymorphic creation → solved using factory or clone.
🔧 Analogy
Constructor (normal): Like walking into a car showroom and saying “I want a Tesla Model 3” → you must know exactly what you want before it’s built.
Polymorphic creation (factory/clone): Like saying “I want a Car, pick the type based on my budget” → the system (factory) decides whether you get a Tesla, BMW, or Toyota at runtime.
📝 Interview Takeaway
Why constructors aren’t virtual? → Because vptr isn’t ready before construction.
Do we ever need virtual constructor? → Sometimes yes, when type is decided at runtime.
How do we solve it? → With factory methods or clone idiom.
Real-world use: Config-driven objects, GUIs, serialization, game engines.
Static Factory Method in C++
✅ 1. What it is (in plain words)
A static factory method is:
A static function (belongs to the class, not to an object)
That creates and returns an object (usually a pointer or smart pointer)
Often used to decide which derived type to create at runtime, especially when you want polymorphic creation.
Key point: It replaces calling constructors directly and gives you flexibility in object creation.
✅ 2. Why “static”?
Static = you don’t need an existing object to call it.
You call it on the class itself:
Base::create(), not on an objectb.create().This makes sense, because the object you want to create doesn’t exist yet.
✅ 3. Why we need it
Suppose you want to do this at runtime:
User chooses
Derived1orDerived2.You only know after the program starts which derived class is required.
You cannot use a virtual constructor (C++ doesn’t allow it).
So you create a static factory method that decides and constructs the correct object.
📝 Virtual Copy Function / Clone Idiom in C++
✅ 1. What it is
A virtual copy function is a virtual method that allows you to make a copy of an object polymorphically.
Usually named
clone()and declared in the base class:
class Base {
public:
virtual Base* clone() const = 0;
virtual ~Base() {}
};
- Each derived class overrides
clone()to return a copy of its own type:
class Derived : public Base {
public:
Derived* clone() const override { return new Derived(*this); }
};
✅ 2. Why it is needed
The problem: normal constructor or copy constructor doesn’t work polymorphically
Base* b = new Derived();
Base* copy = new Base(*b); // ❌ Problem!
Base(*b)→ calls Base copy constructor, notDerivedcopy constructor.This causes object slicing → Derived part is lost.
You cannot just call
Derived(*b)either, because at compile time, you only know it’s a Base pointer.
Solution: virtual copy function
It allows runtime determination of actual type, then calls the correct copy constructor.
Preserves full Derived object, avoiding slicing.
✅ 3. Complete Example
#include <iostream>
using namespace std;
class Base {
public:
virtual Base* clone() const = 0; // virtual copy function
virtual void show() = 0;
virtual ~Base() {}
};
class Derived1 : public Base {
int data;
public:
Derived1(int d) : data(d) {}
Derived1(const Derived1& other) : data(other.data) {}
Base* clone() const override { return new Derived1(*this); }
void show() override { cout << "Derived1, data=" << data << "\n"; }
};
int main() {
Base* b1 = new Derived1(42);
Base* b2 = b1->clone(); // virtual copy preserves Derived1
b1->show(); // Derived1, data=42
b2->show(); // Derived1, data=42
delete b1;
delete b2;
}
✅ Output:
Derived1, data=42
Derived1, data=42
✅ 4. Why you can’t do this with normal copy constructor
Copy constructors work statically: the type must be known at compile time.
When you have a Base pointer, the compiler only sees Base type.
Calling
Base(*b)→ only copies Base part → Derived members are lost (object slicing).Virtual copy function solves this by dispatching the call at runtime to the correct derived type.
🔧 5. Analogy
Think of Base pointer as a mystery gift box.
You know it’s a box, but don’t know its exact contents (could be
ToyCar,Doll, etc.).A normal copy constructor → just copies the box itself, ignoring the contents → incomplete copy.
Virtual copy (
clone()) → asks the actual gift inside the box to make a copy → preserves everything.
✅ 6. Interview Takeaways
Use case: You want to copy an object via a base class pointer or reference without losing derived parts.
How: Implement a pure virtual clone() in base class, override it in all derived classes using
new Derived(*this).Why not normal constructor? → normal constructors are compile-time, don’t know actual derived type, cause slicing.
Works like a virtual constructor for creating copies polymorphically.
⚡ Memorable One-Liner
“The virtual copy function (clone) lets you copy objects polymorphically. Normal constructors can’t do this because the compiler only knows the static type, so using them would slice the derived part.”
📝 Copying an object polymorphically vs normal copy
1️⃣ Normal Copy / Assignment
Suppose you have:
class Base {
public:
int a;
};
class Derived : public Base {
public:
int b;
};
int main() {
Derived d1;
d1.a = 1; d1.b = 2;
Derived d2 = d1; // normal copy constructor
// or
Derived d3;
d3 = d1; // normal assignment
}
✅ Here:
d2andd3are exact copies ofd1.Both Base and Derived parts are copied.
This is fine when the type is known at compile-time.
Object Slicing Example
Base b = d1; // slicing occurs
bis a Base object, so only Base part (a) is copied.Derived part (
b) is lost.This is called object slicing.
2️⃣ Copying polymorphically
Now imagine:
Base* b = new Derived();
Base* copy = new Base(*b); // ❌ Problem
*bhas dynamic type Derived, but the compiler sees only static type Base.Base(*b)→ copies only the Base part → Derived part lost.This is why normal copy cannot preserve the full object when using a base pointer or reference.
✅ Polymorphic copy solves this:
Base* copy = b->clone(); // virtual copy function
b->clone()→ calls Derived::clone() at runtime.Correct Derived copy is created → preserves all members and behavior.
3️⃣ Analogy: mystery gift box
| Scenario | Analogy |
| Normal copy | You know it’s a ToyCar, so you make an identical ToyCar. Works fine. |
| Base object copy | You only know it’s a Toy (base type). Copying gives you a generic toy, losing the car-specific parts. |
| Polymorphic copy | You only know it’s a Toy at compile-time, but at runtime you ask the box “what are you really?” → creates a full copy of the actual toy (ToyCar) including all special features. |
4️⃣ Key Points / Differences
| Feature | Normal copy / assignment | Polymorphic copy (clone) |
| Static type | Known at compile-time | Base pointer/reference only known at compile-time |
| Dynamic type | Ignored | Used at runtime via virtual dispatch |
| Object slicing | Happens if copied through base | Avoided, preserves derived parts |
| Use case | Same-type objects | Copying objects through base pointers polymorphically |
5️⃣ Example with object slicing vs clone
Derived d1(10, 20); // a=10, b=20
Base b = d1; // slicing
cout << b.a; // 10
// cout << b.b; ❌ Error, b has no Derived members
Base* pb = new Derived(10, 20);
Base* copy = pb->clone(); // Polymorphic copy
pb->clone()→ runtime callsDerived::clone()→ preservesbvalue.Object slicing is avoided.
6️⃣ Interview Takeaway
Polymorphic copy = making a full copy of an object through a base pointer/reference, preserving its actual derived type.
Normal copy / assignment works only when type is known at compile-time.
Without polymorphic copy → slicing occurs, derived members lost.
⚡ Memorable one-liner:
“Polymorphic copy preserves the actual derived type when copying through a base pointer; normal copy only sees the base part and slices off the derived portion.”
