A destructor in C++ is a special member function that is called automatically when an object is destroyed. Its main job is cleanup. If a constructor prepares an object for use, the destructor helps finish the object’s life safely by releasing resources, closing files, freeing memory, and performing other final actions before the object disappears.
Destructors matter because many C++ objects manage resources that must not be left hanging. An object may own dynamic memory, a file handle, a socket, a mutex, or some other external resource. If those resources are not cleaned up properly, the program may leak memory, leave files open, or keep system resources locked longer than intended. That is why constructors and destructors are closely connected in C++ class design.
What Is a Destructor in C++?
A destructor is a special member function of a class that runs automatically when an object goes out of scope or is explicitly deleted in the case of dynamic allocation. Its name is the same as the class name, but it begins with a tilde character ~. A destructor has no return type and takes no arguments.
The purpose of a destructor is not general computation. Its purpose is final cleanup. If an object acquired responsibility for some resource during its lifetime, the destructor is the natural place to release that responsibility when the object is no longer needed.
A destructor in C++ is a special member function that runs automatically when an object is destroyed and performs cleanup work before the object’s lifetime ends.
Syntax of Destructor in C++
The destructor syntax is easy to recognize because it uses the class name with a leading tilde.
class ClassName
{
public:
~ClassName()
{
// cleanup work
}
};
Unlike constructors, destructors cannot be overloaded by taking different parameters. A class can have only one destructor. This is because destruction of an object must follow one final cleanup path for that class.
Simple Example of Destructor in C++
The following example shows a constructor and destructor together so that the lifetime of the object becomes easy to observe.
#include <iostream>
using namespace std;
class Demo
{
public:
Demo()
{
cout << "Constructor called" << endl;
}
~Demo()
{
cout << "Destructor called" << endl;
}
};
int main()
{
Demo d;
return 0;
}
When d is created, the constructor runs. When main() ends and d goes out of scope, the destructor runs automatically. There is no need to call the destructor manually in normal stack-based object usage.
Why Destructors Are Needed in C++
Some objects hold simple data such as integers and characters, and those do not need special cleanup. But many classes manage resources that exist outside the ordinary object memory itself. If those resources are not released at the right time, the program can waste memory and operating system resources.
- They release dynamically allocated memory.
- They close opened files.
- They unlock resources or handles.
- They complete cleanup steps before an object disappears.
- They support safer resource management through RAII.
This is why destructors are a central part of reliable C++ programming. They help make cleanup automatic instead of depending on the programmer to remember every release operation manually.
When Is a Destructor Called in C++?
A destructor is called automatically at the end of an object’s lifetime. The exact moment depends on how the object was created.
- For a local object, the destructor runs when the object goes out of scope.
- For a dynamically allocated object, the destructor runs when
deleteis used. - For temporary objects, the destructor runs at the end of the full expression or according to lifetime rules.
- For global or static objects, the destructor runs when the program is shutting down.
Understanding this timing is important because cleanup happens automatically only when object lifetime is managed correctly. For example, a dynamically allocated object will not be destroyed unless it is actually deleted, or unless a smart pointer or other owner destroys it.
Destructor with Dynamic Memory Example
One of the classic uses of a destructor is freeing memory allocated with new. If a class allocates memory during construction or later during its lifetime, the destructor is often responsible for releasing that memory using delete or delete[].
#include <iostream>
using namespace std;
class ArrayHolder
{
private:
int* data;
public:
ArrayHolder()
{
data = new int[5];
cout << "Memory allocated" << endl;
}
~ArrayHolder()
{
delete[] data;
cout << "Memory released" << endl;
}
};
int main()
{
ArrayHolder obj;
return 0;
}
Here, the destructor prevents a memory leak. If delete[] data; were missing, the allocated memory would remain unreleased after the object is destroyed. This is one of the classic reasons destructors exist.
Destructor for File or Resource Cleanup
Cleanup is not limited to heap memory. A destructor can also close files, release network connections, or stop device sessions. Any resource that must be matched with a cleanup step can often be managed through a destructor.
This is one reason RAII is so powerful in C++. A class can acquire a resource in its constructor and release it in its destructor. Then the resource becomes tied to object lifetime, which makes the code more reliable even when exceptions or early returns occur.
Characteristics of a Destructor in C++
- The destructor name is the class name with
~before it. - It has no return type.
- It takes no parameters.
- A class can have only one destructor.
- It is called automatically when the object’s lifetime ends.
These rules make destructors easy to identify and help separate them clearly from ordinary member functions.
Destructor vs Constructor in C++
| Point | Constructor | Destructor |
|---|---|---|
| Main purpose | Initialize object state | Clean up object state and resources |
| Name style | Same as class name | Same as class name with ~ |
| Arguments | May take arguments | Takes no arguments |
| Overloading | Can be overloaded | Cannot be overloaded |
| When called | At object creation | At object destruction |
Constructors and destructors are complementary. One begins the object lifetime, and the other finishes it. Good class design usually considers both together.
Order of Constructor and Destructor Calls
When multiple local objects are created in a scope, constructors run in the order of creation, while destructors run in the reverse order. This reverse cleanup order is important because later objects may depend on earlier ones.
#include <iostream>
using namespace std;
class Test
{
private:
int id;
public:
Test(int value)
{
id = value;
cout << "Constructor " << id << endl;
}
~Test()
{
cout << "Destructor " << id << endl;
}
};
int main()
{
Test a(1);
Test b(2);
return 0;
}
The output will show constructor 1, constructor 2, then destructor 2, destructor 1. This reverse order helps maintain safe cleanup behavior.
Compiler-Generated Destructor in C++
If a class does not define its own destructor, the compiler usually provides a default destructor. For classes that only contain simple members and do not manage special resources, that compiler-generated destructor may be completely fine. It will destroy the members according to language rules.
However, if a class owns a resource that needs explicit release logic, relying blindly on the compiler-generated destructor may be insufficient. In such cases, a user-defined destructor becomes necessary so the class can perform proper cleanup work.
Virtual Destructor in C++
When inheritance is involved and objects may be deleted through a base-class pointer, destructors need extra care. If the base class destructor is not virtual, deleting a derived object through a base pointer can lead to incomplete cleanup. That can cause resource leaks or undefined behavior depending on the design.
A virtual destructor ensures that the correct derived-class destructor runs first, followed by the base-class destructor. This is a critical rule in polymorphic base classes.
class Base
{
public:
virtual ~Base()
{
}
};
This topic becomes even more important in inheritance and polymorphism discussions, but it is useful to introduce it here because destructor behavior is directly tied to correct object cleanup.
Destructor and RAII in C++
RAII stands for Resource Acquisition Is Initialization. In this style, a resource is tied to object lifetime. The constructor acquires the resource, and the destructor releases it. This pattern is one of the strongest ideas in C++ because it makes cleanup automatic and exception-safe in many situations.
For example, instead of manually calling open and close functions all over the program, a class can open a file in its constructor and close it in its destructor. Then the programmer only needs to manage the object’s lifetime, and the resource cleanup follows automatically.
Common Mistakes with Destructors in C++
- Trying to give the destructor a return type.
- Trying to overload the destructor with parameters.
- Forgetting to release dynamically allocated resources.
- Deleting through a base pointer without a virtual destructor in polymorphic designs.
- Manually calling a destructor in ordinary situations where automatic lifetime management should be used.
One especially harmful beginner mistake is forgetting that using new usually requires a matching cleanup strategy. If a class owns the memory, its destructor should usually participate in releasing it. If ownership is unclear, cleanup bugs appear quickly.
Best Practices for Destructors in C++
- Write a destructor when the class owns resources that need explicit cleanup.
- Keep destructor logic focused on release and final cleanup.
- Prefer RAII and automatic lifetime management where possible.
- Use a virtual destructor in base classes meant for polymorphic deletion.
- Avoid complex operations in destructors unless they are truly necessary for cleanup.
A good destructor should make the end of object lifetime predictable and safe. If a class owns something important, destroying the object should also cleanly release what it owns.
Why Destructors Matter in Real C++ Programs
Destructors matter because real C++ software often manages resources that are not automatically handled by simple variable scope alone. Memory, files, locks, sockets, and device sessions all need disciplined cleanup. A destructor gives the class a guaranteed point where that cleanup can happen as the object’s lifetime ends.
That makes destructors much more than a language formality. They are part of the reason C++ can express strong ownership and resource control. Once constructors explain how an object begins its life, destructors explain how that life ends safely and cleanly.
Continue learning C++ in order
Follow the topic sequence with the previous and next lesson.