C++98corebeginner
Destructors
C++98 · The Foundation · Compiler flag: -std=c++98
Explanation
Special functions called when an object is destroyed to clean up resources.
Real-World Scenario
Close a file handle when a file object is deleted.
Quick Snippet
snippet.cpp
class File { public: ~File() {} };Full Example
destructors.cpp
// CodeNebula - C++98 Example: Destructors
// Compile: g++ -std=c++98 -Wall -Wextra -o example destructors.cpp
//
// This example demonstrates:
// - How destructors are called automatically when objects leave scope
// - Resource cleanup (heap memory, simulated file handles)
// - Destruction order: reverse of construction order
// - Destructors in inheritance hierarchies (virtual destructor importance)
#include <iostream>
#include <cstring>
// ============================================================
// 1. Basic destructor with resource cleanup
// ============================================================
class ManagedBuffer {
private:
char* buffer;
int capacity;
const char* label;
public:
ManagedBuffer(const char* lbl, int cap)
: buffer(new char[cap]), capacity(cap), label(lbl)
{
std::memset(buffer, 0, capacity);
std::cout << " [ManagedBuffer \"" << label
<< "\"] Constructed (" << capacity << " bytes)" << std::endl;
}
~ManagedBuffer() {
std::cout << " [ManagedBuffer \"" << label
<< "\"] Destructor: freeing " << capacity << " bytes" << std::endl;
delete[] buffer;
}
void write(const char* text) {
std::strncpy(buffer, text, capacity - 1);
buffer[capacity - 1] = '\0';
}
void print() const {
std::cout << " [ManagedBuffer \"" << label << "\"] content: \""
<< buffer << "\"" << std::endl;
}
};
// ============================================================
// 2. Destruction order demonstration
// ============================================================
class OrderTracker {
private:
int id;
public:
OrderTracker(int i) : id(i) {
std::cout << " OrderTracker #" << id << " constructed" << std::endl;
}
~OrderTracker() {
std::cout << " OrderTracker #" << id << " DESTROYED" << std::endl;
}
};
// ============================================================
// 3. Virtual destructor in inheritance
// ============================================================
class Base {
private:
int* baseData;
public:
Base() : baseData(new int(42)) {
std::cout << " Base constructor (allocated baseData)" << std::endl;
}
// Virtual destructor ensures derived destructor is called
// when deleting through a Base pointer.
virtual ~Base() {
std::cout << " Base destructor (freeing baseData)" << std::endl;
delete baseData;
}
virtual void describe() const {
std::cout << " I am Base, baseData = " << *baseData << std::endl;
}
};
class Derived : public Base {
private:
int* derivedData;
public:
Derived() : Base(), derivedData(new int(99)) {
std::cout << " Derived constructor (allocated derivedData)" << std::endl;
}
~Derived() {
std::cout << " Derived destructor (freeing derivedData)" << std::endl;
delete derivedData;
}
void describe() const {
std::cout << " I am Derived, derivedData = " << *derivedData << std::endl;
}
};
// ============================================================
// 4. Non-virtual destructor problem (for educational contrast)
// ============================================================
class BadBase {
public:
BadBase() { std::cout << " BadBase constructed" << std::endl; }
// NOTE: destructor is NOT virtual on purpose -- this is the bug
~BadBase() { std::cout << " BadBase destructor" << std::endl; }
};
class BadDerived : public BadBase {
private:
int* leak;
public:
BadDerived() : BadBase(), leak(new int(7)) {
std::cout << " BadDerived constructed (allocated leak)" << std::endl;
}
~BadDerived() {
std::cout << " BadDerived destructor (freeing leak)" << std::endl;
delete leak;
}
};
int main() {
std::cout << "========================================" << std::endl;
std::cout << "1. BASIC DESTRUCTOR & RESOURCE CLEANUP" << std::endl;
std::cout << "========================================" << std::endl;
{
ManagedBuffer buf("demo", 64);
buf.write("Hello, C++98!");
buf.print();
std::cout << " -- leaving scope --" << std::endl;
}
std::cout << " (buffer has been freed)\n" << std::endl;
std::cout << "========================================" << std::endl;
std::cout << "2. DESTRUCTION ORDER (reverse of construction)" << std::endl;
std::cout << "========================================" << std::endl;
{
OrderTracker a(1);
OrderTracker b(2);
OrderTracker c(3);
std::cout << " -- leaving scope --" << std::endl;
}
std::cout << " (destroyed 3, 2, 1 -- reverse order)\n" << std::endl;
std::cout << "========================================" << std::endl;
std::cout << "3. VIRTUAL DESTRUCTOR (correct cleanup)" << std::endl;
std::cout << "========================================" << std::endl;
{
std::cout << " Creating Derived via Base pointer:" << std::endl;
Base* ptr = new Derived();
ptr->describe();
std::cout << " Deleting via Base pointer:" << std::endl;
delete ptr; // Calls ~Derived() then ~Base() thanks to virtual
}
std::cout << " (Both Derived and Base destructors ran)\n" << std::endl;
std::cout << "========================================" << std::endl;
std::cout << "4. NON-VIRTUAL DESTRUCTOR (memory leak!)" << std::endl;
std::cout << "========================================" << std::endl;
{
std::cout << " Creating BadDerived via BadBase pointer:" << std::endl;
BadBase* ptr = new BadDerived();
std::cout << " Deleting via BadBase pointer:" << std::endl;
delete ptr; // Only ~BadBase() runs! ~BadDerived() is SKIPPED -> leak
}
std::cout << " (Only BadBase destructor ran -- BadDerived leaked!)" << std::endl;
std::cout << " LESSON: Always make base-class destructors virtual" << std::endl;
std::cout << " if the class will be used polymorphically.\n" << std::endl;
std::cout << "========================================" << std::endl;
std::cout << "5. MEMBER OBJECTS DESTRUCTION ORDER" << std::endl;
std::cout << "========================================" << std::endl;
{
std::cout << " Creating two ManagedBuffers in a scope:" << std::endl;
ManagedBuffer first("first", 32);
ManagedBuffer second("second", 16);
first.write("I was created first");
second.write("I was created second");
first.print();
second.print();
std::cout << " -- leaving scope --" << std::endl;
}
std::cout << " (\"second\" destroyed before \"first\")" << std::endl;
return 0;
}
// Expected output (addresses will vary):
// ========================================
// 1. BASIC DESTRUCTOR & RESOURCE CLEANUP
// ========================================
// [ManagedBuffer "demo"] Constructed (64 bytes)
// [ManagedBuffer "demo"] content: "Hello, C++98!"
// -- leaving scope --
// [ManagedBuffer "demo"] Destructor: freeing 64 bytes
// (buffer has been freed)
//
// ========================================
// 2. DESTRUCTION ORDER (reverse of construction)
// ========================================
// OrderTracker #1 constructed
// OrderTracker #2 constructed
// OrderTracker #3 constructed
// -- leaving scope --
// OrderTracker #3 DESTROYED
// OrderTracker #2 DESTROYED
// OrderTracker #1 DESTROYED
// (destroyed 3, 2, 1 -- reverse order)
//
// ========================================
// 3. VIRTUAL DESTRUCTOR (correct cleanup)
// ========================================
// Creating Derived via Base pointer:
// Base constructor (allocated baseData)
// Derived constructor (allocated derivedData)
// I am Derived, derivedData = 99
// Deleting via Base pointer:
// Derived destructor (freeing derivedData)
// Base destructor (freeing baseData)
// (Both Derived and Base destructors ran)
//
// ========================================
// 4. NON-VIRTUAL DESTRUCTOR (memory leak!)
// ========================================
// Creating BadDerived via BadBase pointer:
// BadBase constructed
// BadDerived constructed (allocated leak)
// Deleting via BadBase pointer:
// BadBase destructor
// (Only BadBase destructor ran -- BadDerived leaked!)
// LESSON: Always make base-class destructors virtual
// if the class will be used polymorphically.
//
// ========================================
// 5. MEMBER OBJECTS DESTRUCTION ORDER
// ========================================
// Creating two ManagedBuffers in a scope:
// [ManagedBuffer "first"] Constructed (32 bytes)
// [ManagedBuffer "second"] Constructed (16 bytes)
// [ManagedBuffer "first"] content: "I was created first"
// [ManagedBuffer "second"] content: "I was created second"
// -- leaving scope --
// [ManagedBuffer "second"] Destructor: freeing 16 bytes
// [ManagedBuffer "first"] Destructor: freeing 32 bytes
// ("second" destroyed before "first")
Run command
g++ -std=c++98 -Wall -Wextra -o example destructors.cpp