- References
- Function Overloading
- Default Function Arguments
- Inline Function
- Dynamic Memory Allocation
- Placement New
- nullptr
- Namespaces
- Type Aliases
- Enum classes
- constexpr
- static_assert
- mutable Keyword
- auto Keyword
- Smart Pointers
- Basics of Classes
- Constructors
- Destructors
- Operator Overloading
- Copy Semantics
- Move Semantics
- Composition, RAII & Ownership
- Inheritance
- Polymorphism
- Abstraction
- Encapsulation
- Template
- Static Memory
- Friend Function
- this Pointer
- Function Pointer
- Lambdas and Callback Management
- Union
Destructors (Automatic Cleanup)

A Destructor is a special member function that runs automatically when an object goes out of scope (e.g., function returns, block ends) or is explicitly deleted.
Its purpose is to release resources (memory, hardware locks, file handles) held by the object.
It has the same name as the class prefixed with a tilde (~), takes no arguments, and has no return type.
Syntax & Usage
1. Basic Destructor
Cleaning up when a "Scope" ends.
class LED {
int pin;
public:
LED(int p) : pin(p) {
gpio_write(pin, HIGH); // Turn ON when created
}
// Destructor
~LED() {
gpio_write(pin, LOW); // Turn OFF when destroyed
}
};
void blink() {
LED status_led(13); // Constructor: LED turns ON
delay(100);
} // End of scope: Destructor runs -> LED turns OFF automatically2. RAII (The Scope Lock Pattern)
This is the most critical use case in firmware. It guarantees a resource is released even if you return early.
class MutexLock {
Mutex& mtx;
public:
MutexLock(Mutex& m) : mtx(m) { mtx.lock(); } // Acquire
~MutexLock() { mtx.unlock(); } // Release
};
void critical_function() {
MutexLock lock(system_mutex); // Mutex locked here
if (error_detected) return; // ✅ Safe! Destructor unlocks mutex automatically.
process_data();
} // Destructor unlocks mutex here normally.Lifecycle Visualization
| Event | Action |
|---|---|
void func() { | Start of scope. |
Obj x; | Constructor runs (Resource acquired). |
... | Code executes. |
} | End of scope. Destructor runs (Resource released). |
Relevance in Embedded/Firmware
1. Resource Safety (RAII)
In C, if you forget to call Unlock() or Disable() before a return statement, your system deadlocks. Destructors automate this.
- Interrupts: Disable in Constructor, Enable in Destructor.
- Power: Wake up peripheral in Constructor, Sleep in Destructor.
2. Virtual Destructors (Polymorphism)
If you use Interfaces (Abstract Classes) in your HAL, you must make the destructor virtual.
If you don't, deleting a derived driver through a base pointer will not call the derived destructor, causing resource leaks (e.g., UART not turning off).
class IDriver {
public:
virtual ~IDriver() {} // ✅ Essential for Interfaces
};Common Pitfalls (Practical Tips)
| Pitfall | Details |
|---|---|
| ❌ Virtual Destructor | If a class has virtual functions, it must have a virtual destructor. Otherwise, cleaning up via a Base pointer (Base* b = new Derived(); delete b;) will fail to clean up the Derived part. |
| ❌ Exceptions | Never throw an exception inside a destructor. If a destructor crashes while another exception is being handled, the program terminates immediately. |
| ❌ Manual Call | Never call a destructor manually (obj.~Class()) unless you used Placement New. The compiler calls it for you. Calling it twice causes undefined behavior (double-free). |
| ✅ Order of Destruction | Objects are destroyed in the reverse order of creation.
|
Concept understood? Let's apply and learn for real