Destructors
Overview
If a Constructor is executed when an Object is born, a Destructor is executed when the Object dies.
When a function ends, all objects created inside it are purged from Stack memory. The exact microsecond before the OS deletes the object, it calls the Destructor. This is absolutely critical in C++ because if your Object dynamically allocated Gigabytes of Heap Memory (using new), the OS will NOT clean it up automatically. The Destructor is your final, guaranteed opportunity to call delete and prevent catastrophic Memory Leaks.
Syntax
#include <iostream>
class NetworkConnection {
private:
int* dataBuffer;
public:
// CONSTRUCTOR: Born!
NetworkConnection() {
std::cout << "Opening Connection...\n";
dataBuffer = new int[1000]; // Allocating Heap Memory!
}
// DESTRUCTOR: Dying! (Indicated by the Tilde ~)
~NetworkConnection() {
std::cout << "Closing Connection & Freeing Memory...\n";
delete[] dataBuffer; // CRITICAL: Preventing Memory Leaks!
}
};
int main() {
std::cout << "--- Scope Begins ---\n";
{
NetworkConnection conn;
// We do stuff with the connection here...
}
// Scope ends! 'conn' is destroyed, automatically triggering ~NetworkConnection()
std::cout << "--- Scope Ends ---\n";
return 0;
}Common Pitfalls
- Forgetting to delete Heap memory inside the Destructor. If your Class manages raw pointers and dynamic memory, failing to write a Destructor virtually guarantees your application will leak memory and crash the server over time.
- Trying to call the Destructor manually. You should almost NEVER write
myObject.~MyClass();. The compiler automatically handles the destruction sequence based on variable scope. Calling it manually causes Double-Free memory corruption.
Interview Questions
No. A Class can have infinite Constructor overloads to handle different startup scenarios, but it can only ever have ONE Destructor. The Destructor takes absolutely zero parameters, making it mathematically impossible to overload.
Real-World Example
Implementing RAII (Resource Acquisition Is Initialization), the most important design pattern in C++. It ties the lifespan of a secure resource (like a locked File) directly to the lifespan of a localized Object.
#include <iostream>
class FileHandler {
public:
FileHandler() {
std::cout << "File physically Locked for writing.\n";
}
~FileHandler() {
// Guaranteed to run, even if the program throws an error!
std::cout << "File Unlocked automatically!\n";
}
};
void processFile() {
FileHandler file; // Constructor locks it
// Do dangerous processing here...
} // Destructor automatically unlocks it!
int main() {
processFile();
return 0;
}Check Your Knowledge
Test your understanding of Destructors with these quick questions.