Characteristics of a Destructor
A destructor in C++ has the following characteristics:
1. It has the same name as the class, preceded by a tilde (~) symbol.
2. It does not take any arguments.
3. It does not have a return type, not even void.
4. It is automatically called when an object is destroyed or goes out of scope.
5. It is responsible for cleaning up any resources the object allocates during its lifetime.
6. If not explicitly defined, the compiler provides a default destructor that does nothing.
7. There can be only one destructor per class.
8. It cannot be overloaded, meaning you cannot have multiple destructors with different parameters.
9. It can be declared as virtual to ensure proper cleanup in derived classes.
Examples of Destructor
Let's look at some examples to understand how destructors work in our programs :
Example 1: Simple Destructor
class MyClass {
private:
int* ptr;
public:
MyClass() {
ptr = new int;
cout << "Constructor called" << endl;
}
~MyClass() {
delete ptr;
cout << "Destructor called" << endl;
}
};
int main() {
MyClass obj;
return 0;
}

You can also try this code with Online C++ Compiler
Run Code
Output:
Constructor called
Destructor called
In this example, the class `MyClass` has a constructor that allocates memory for an integer using the `new` keyword. The destructor `~MyClass()` is responsible for deallocating the memory using the `delete` keyword. When the object `obj` goes out of scope at the end of the `main()` function, the destructor is automatically called, freeing the allocated memory.
Example 2: Destructor in Inheritance
class Base {
public:
Base() { cout << "Base constructor" << endl; }
virtual ~Base() { cout << "Base destructor" << endl; }
};
class Derived : public Base {
public:
Derived() { cout << "Derived constructor" << endl; }
~Derived() { cout << "Derived destructor" << endl; }
};
int main() {
Base* obj = new Derived();
delete obj;
return 0;
}

You can also try this code with Online C++ Compiler
Run Code
Output
Base constructor
Derived constructor
Derived destructor
Base destructor
In this example, we have a base class, `Base,` and a derived class, `Derived.` The base class destructor is declared as `virtual` to ensure that the derived class destructor is called when deleting an object through a base class pointer. When the `delete` operator is used on the base class pointer, `obj,` it invokes the derived class destructor first, followed by the base class destructor.
When is the destructor called?
The destructor in C++ is automatically called in the following situations:
1. When a local object goes out of scope:
void someFunction() {
MyClass obj; // Constructor called
// ...
} // Destructor called when obj goes out of scope
In this case, the destructor for `obj` is called when the function `someFunction()` ends & the object goes out of scope.
2. When an object allocated with `new` is explicitly deleted:
MyClass* obj = new MyClass(); // Constructor called
// ...
delete obj; // Destructor called when delete is used
When an object is dynamically allocated using the `new` keyword, the destructor is called when the `delete` operator is used on the object pointer.
3. When a program ends & global/static objects are destroyed:
MyClass globalObj; // Constructor called
int main() {
// ...
return 0;
} // Destructor called for globalObj when the program ends
Global & static objects are destroyed when the program ends, & their destructors are called at that point.
4. When a container object is destroyed:
std::vector<MyClass> myVector;
myVector.push_back(MyClass()); // Constructor called
// ...
// Destructor called for each object in myVector when the vector is destroyed
When a container object, such as a vector or a map, is destroyed, the destructors for all the objects stored within the container are automatically called.
It's important to note that the destructor is called automatically in these situations, and you don't need to invoke it explicitly. The destructor ensures that any necessary cleanup tasks are performed before the object is destroyed, such as releasing dynamically allocated memory or closing file handles.
How do we call destructors explicitly?
In C++, you can call a destructor explicitly using the `delete` operator. However, it's important to note that explicitly calling a destructor is not a common practice & should be done with caution.
Let’s look at the scenarios where you might explicitly call a destructor:
1. When using placement new
char* buffer = new char[sizeof(MyClass)];
MyClass* obj = new (buffer) MyClass(); // Placement new
// ...
obj->~MyClass(); // Explicitly calling the destructor
delete[] buffer;
In this case, the object `obj` is created using placement new, which constructs the object in a pre-allocated memory buffer. To destroy the object explicitly, you can directly call the destructor using the `obj->~MyClass()` syntax.
2. When creating objects in raw memory
char* buffer = new char[sizeof(MyClass)];
MyClass* obj = reinterpret_cast<MyClass*>(buffer);
// ...
obj->~MyClass(); // Explicitly calling the destructor
delete[] buffer;
If you create an object in raw memory using techniques like `reinterpret_cast,` you must explicitly call the destructor to ensure proper cleanup.
It's crucial to remember that when you explicitly call a destructor, you are responsible for managing the memory & ensuring that the destructor is called only once for each object. Explicitly calling a destructor on an object that has already been destroyed or on memory that hasn't been properly allocated can lead to undefined behavior.
Note: In most cases, it's recommended that the compiler handle object destruction automatically using smart pointers, containers, or the natural scope and lifetime of objects.
When do we need to write a user-defined destructor?
In many cases, the compiler-provided default destructor is sufficient for cleaning up objects. However, there are situations when you need to write a user-defined destructor.
Let’s look at the few of the scenarios:
1. Freeing dynamically allocated memory
If your class dynamically allocates memory using `new,` you must write a destructor to properly deallocate that memory using `delete` to avoid memory leaks.
class MyClass {
private:
int* ptr;
public:
MyClass() {
ptr = new int;
}
~MyClass() {
delete ptr;
}
};
2. Releasing acquired resources
If your class acquires resources such as file handles, network connections, or system handles, you must write a destructor to release those resources when the object is destroyed.
class FileHandler {
private:
FILE* file;
public:
FileHandler(const char* filename) {
file = fopen(filename, "r");
}
~FileHandler() {
if (file != nullptr) {
fclose(file);
}
}
};
3. Performing custom cleanup tasks
If your class has specific cleanup tasks that must be performed when an object is destroyed, you can write a destructor to handle those tasks.
class DatabaseConnection {
public:
~DatabaseConnection() {
// Close the database connection
// Perform any necessary cleanup tasks
}
};
4. Virtual destructors in inheritance hierarchies
If you have a base class that is intended to be inherited from & you expect derived classes to have their own destructors, you should declare the base class destructor as virtual. This ensures that the correct destructor is called when deleting an object through a base class pointer.
class Base {
public:
virtual ~Base() {
// Base class cleanup
}
};
class Derived : public Base {
public:
~Derived() {
// Derived class cleanup
}
};
When you are writing a user-defined destructor, you have control over the cleanup process. You need to ensure that any necessary resources are properly released and any custom cleanup tasks are performed when an object is destroyed.
Default Destructor and User-Defined C++ Destructor
In C++, if you do not explicitly define a destructor for a class, the compiler provides a default destructor. The default destructor is an implicit member function that performs no explicit action. It is sufficient for classes with no dynamically allocated memory or resources that must be manually released.
Let’s look at an example of a class with a default destructor:
class MyClass {
private:
int value;
public:
MyClass(int val) : value(val) {}
// Default destructor (implicitly provided by the compiler)
};
In this case, the compiler generates a default destructor for the `MyClass` class, which does nothing.
On the other hand, if your class requires specific cleanup tasks or needs to release dynamically allocated resources, you need to define a user-defined destructor. A user-defined destructor allows you to perform custom cleanup tasks when an object is destroyed.
For example :
class MyClass {
private:
int* ptr;
public:
MyClass() {
ptr = new int;
}
~MyClass() {
delete ptr;
}
};
In this example, the `MyClass` class dynamically allocates memory for an integer using `new` in the constructor. To ensure proper memory deallocation, a user-defined destructor `~MyClass()` is implemented. The destructor uses `delete` to free the memory pointed to by `ptr`.
It's important to note that the compiler does not provide a default destructor if you define any constructor for a class (default, parameterized, or copy constructor). In such cases, if your class requires a destructor, you need to define it explicitly.
Also, if a class has any non-static data members that are objects of a class with a destructor, or if the class is derived from a base class with a destructor, the compiler will automatically define a destructor for the class (if not provided explicitly).
Virtual C++ Destructor
In C++, a destructor can be declared as virtual to ensure that the appropriate destructor is called for objects of derived classes when they are deleted through a pointer to the base class. This is important in inheritance hierarchies to ensure proper cleanup and avoid memory leaks.
For example :
class Base {
public:
virtual ~Base() {
cout << "Base destructor" << endl;
}
};
class Derived : public Base {
public:
~Derived() {
cout << "Derived destructor" << endl;
}
};
int main() {
Base* obj = new Derived();
delete obj;
return 0;
}

You can also try this code with Online C++ Compiler
Run Code
In this example, we have a base class `Base` and a derived class `Derived`. The `Base` class has a virtual destructor `~Base()`, and the `Derived` class has its own destructor `~Derived()`.
When we create an object of the `Derived` class using a pointer to the `Base` class (`Base* obj = new Derived();`), and then delete that object using `delete obj;`, the virtual destructor ensures that the destructor of the `Derived` class is called before the destructor of the `Base` class.
Output:
Derived destructor
Base destructor
If the destructor in the `Base` class were not declared as virtual, the destructor of the `Derived` class would not be called when deleting the object through a pointer to the `Base` class, potentially leading to memory leaks or improper cleanup.
Always Remember: It's a good practice to make the destructor virtual in the base class if you intend to inherit from it and have derived classes with their own destructors. This way, you ensure that the correct destructor is called when deleting objects through base class pointers, maintaining proper cleanup and preventing resource leaks.
Rules for C++ Destructors
When working with destructors in C++, many important rules must be remembered. Let’s discuss those rules now :
1. Destructor name: The destructor must have the same name as the class, preceded by a tilde (~) symbol.
2. No return type: Destructors do not have a return type, not even void.
3. No parameters: Destructors cannot take any parameters. They are always called without arguments.
4. Only one destructor per class: A class can have only one destructor. Overloading destructors is not allowed.
5. Destructor visibility: Destructors should generally be declared in the public section of the class to allow proper cleanup when the object is destroyed.
6. Virtual destructors: If a class is intended to be used as a base class and has virtual functions, it is recommended to make the destructor virtual to ensure proper cleanup of derived objects when deleted through a base class pointer.
7. Explicit destructor call: Destructors are automatically called when an object is destroyed. Explicitly calling a destructor using the delete operator is only necessary when using placement new or managing objects in raw memory.
8. No exceptions: Destructors should not throw exceptions. If a destructor throws an exception, it may lead to unexpected behavior and resource leaks.
9. Destructor and inheritance: If a class has a destructor and is inherited by another class, the derived class should also define its own destructor to perform any necessary cleanup specific to the derived class.
10. Destructor and polymorphism: If a class has virtual functions and is used in a polymorphic context, it is essential to declare the destructor as virtual to ensure the correct destructor is called when deleting objects through base class pointers.
Use of C++ Destructor
1. Releasing dynamically allocated memory: Destructors are used to deallocate memory that was dynamically allocated using `new` within the class. This prevents memory leaks and ensures proper cleanup of resources.
2. Closing file handles: If a class opens files or acquires file handles, the destructor is responsible for closing those files or releasing the handles. This frees up system resources and prevents resource leaks.
3. Releasing acquired resources: Destructors are used to release any resources acquired by the class, such as network connections, database connections, or system handles. This ensures that resources are correctly released when the object is destroyed.
4. Performing cleanup tasks: Destructors can perform any necessary cleanup tasks specific to the class like resetting data members, updating external state, or sending notifications.
5. Maintaining the RAII (Resource Acquisition Is Initialization) principle: Destructors play a crucial role in implementing the RAII principle, which states that resources should be acquired in the constructor and released in the destructor. This ensures that resources are properly managed throughout an object's lifetime.
6. Enabling proper cleanup in inheritance hierarchies: When a class is used as a base class and derived classes have their own cleanup requirements, declaring the base class destructor as virtual ensures that the appropriate destructor is called when deleting objects through base class pointers. This allows for proper cleanup and prevents resource leaks in polymorphic scenarios.
Frequently Asked Questions
Can a destructor be overloaded?
No, a class can have only one destructor. Unlike constructors, destructors cannot be overloaded.
Is it necessary to declare a destructor as virtual?
It is recommended to declare a destructor as virtual if a class is intended to be used as a base class with virtual functions. This ensures that the correct destructor is called when deleting objects through base class pointers.
Can a destructor throw exceptions?
It is generally not recommended for destructors to throw exceptions. If a destructor throws an exception, it may lead to unexpected behavior and resource leaks. Destructors should handle exceptions internally and not let them propagate.
Conclusion
In this article, we discussed the concept of destructors in C++. We learned about their syntax and characteristics, when they are called, and how to define them. We also discussed the differences between default and user-defined destructors, the importance of virtual destructors in inheritance hierarchies, and the key rules to follow when working with destructors. Destructors play a vital role in resource management, cleanup tasks, and ensures proper object destruction in C++ programs.
You can also check out our other blogs on Code360.