C++ Virtual Destructor | 'Memory Leak' Inherited Class Destructor Error Solution

C++ Virtual Destructor | 'Memory Leak' Inherited Class Destructor Error Solution

이 글의 핵심

C++ virtual destructor C++, destructor, "memory, Introduction: "Deleted derived class but got memory leak" explained in detail with practical examples.

Introduction: “Deleted Derived Class but Got Memory Leak"

"Deleted with base class pointer but destructor not called”

In C++, when deleting derived class through base class pointer, without virtual destructor, derived class destructor is not called causing memory leak.

// ❌ No virtual destructor
class Base {
public:
    ~Base() {  // Non-virtual destructor
        std::cout << "~Base\n";
    }
};

class Derived : public Base {
    int* data_;
public:
    Derived() : data_(new int[1000]) {}
    
    ~Derived() {
        delete[] data_;  // Not called!
        std::cout << "~Derived\n";
    }
};

int main() {
    Base* ptr = new Derived();
    delete ptr;  // ❌ ~Derived not called → memory leak
    // Output: ~Base
}

What This Guide Covers:

  • Why virtual destructor is needed
  • Memory leak and undefined behavior
  • Pure virtual destructor
  • protected destructor

1. Why Virtual Destructor is Needed

Problem: Non-virtual Destructor

Here is the main implementation:

// ❌ Non-virtual destructor
class Base {
public:
    ~Base() {
        std::cout << "~Base\n";
    }
};

class Derived : public Base {
public:
    ~Derived() {
        std::cout << "~Derived\n";
    }
};

int main() {
    Base* ptr = new Derived();
    delete ptr;  // ❌ Undefined behavior
    // Output: ~Base (derived class destructor not called)
}

Solution: Virtual Destructor

Here is the main implementation:

// ✅ Virtual destructor
class Base {
public:
    virtual ~Base() {
        std::cout << "~Base\n";
    }
};

class Derived : public Base {
public:
    ~Derived() override {
        std::cout << "~Derived\n";
    }
};

int main() {
    Base* ptr = new Derived();
    delete ptr;  // ✅ Correct destructor called
    // Output:
    // ~Derived
    // ~Base
}

2. Memory Leak Examples

Example 1: Dynamic Memory

Here is detailed implementation code using C++. Define a class to encapsulate data and functionality, process data with loops. Understand the role of each part while examining the code.

// ❌ Memory leak
class Base {
public:
    ~Base() {}
};

class Derived : public Base {
    int* data_;
public:
    Derived() : data_(new int[1000000]) {
        std::cout << "Allocated 4MB\n";
    }
    
    ~Derived() {
        delete[] data_;  // Not called!
        std::cout << "Freed 4MB\n";
    }
};

int main() {
    for (int i = 0; i < 100; ++i) {
        Base* ptr = new Derived();
        delete ptr;  // ❌ 4MB leak × 100 = 400MB leak
    }
}

Example 2: File Handle

Here is detailed implementation code using C++. Define a class to encapsulate data and functionality. Understand the role of each part while examining the code.

// ❌ File handle leak
class Base {
public:
    ~Base() {}
};

class FileLogger : public Base {
    std::ofstream file_;
public:
    FileLogger(const std::string& path) : file_(path) {}
    
    ~FileLogger() {
        file_.close();  // Not called!
        std::cout << "File closed\n";
    }
};

int main() {
    Base* ptr = new FileLogger("log.txt");
    delete ptr;  // ❌ File handle leak
}

3. Pure Virtual Destructor

Pure Virtual Destructor

Pure virtual destructor makes class abstract but must provide definition.

Here is detailed implementation code using C++. Define a class to encapsulate data and functionality. Understand the role of each part while examining the code.

// Pure virtual destructor
class Base {
public:
    virtual ~Base() = 0;  // Pure virtual
};

// Definition required
Base::~Base() {
    std::cout << "~Base\n";
}

class Derived : public Base {
public:
    ~Derived() override {
        std::cout << "~Derived\n";
    }
};

int main() {
    // Base b;  // Compile error: abstract class
    Base* ptr = new Derived();
    delete ptr;  // OK
}

When to use: Want to make abstract class without other pure virtual functions.


Summary

Key Points

  1. Virtual destructor: Needed for polymorphic base classes
  2. Memory leak: Occurs without virtual destructor
  3. Pure virtual destructor: Makes class abstract
  4. protected destructor: Prevents deletion through base pointer
  5. Performance: 8-byte vtable pointer overhead

When to Use

Use virtual destructor when:

  • Base class for inheritance
  • Polymorphic usage expected
  • Delete through base pointer

Don’t use when:

  • No inheritance
  • No polymorphism
  • Performance critical (avoid vtable)

Best Practices

  • ✅ Always virtual destructor for polymorphic base
  • ✅ Use override keyword
  • ✅ Consider protected destructor alternative
  • ❌ Don’t forget virtual in base class
  • ❌ Don’t delete derived through non-virtual base

Master virtual destructors for safer C++ inheritance! 🚀