A destructor is a special member function of a class that is called if an object of that class gets out of scope or when the delete expression is used on a pointer to that class's object. A destructor will have the same name as the class, but it will be prefixed with a tilde (), and it will not be able to return any value or take any parameters. Destructor can be extremely useful for releasing resizable objects.
Destructor is useful for releasing resources before exiting a program, such as closing files and releasing memories.
Use of C++ Destructor
In C++, destructors play a crucial role in resource management, ensuring that resources allocated by an object are properly released when the object is no longer needed. Here are some of the primary uses of C++ destructors:
1. Memory Deallocation
Destructors are commonly used to deallocate memory that was allocated during the lifetime of an object. This prevents memory leaks by ensuring that dynamically allocated memory is properly freed.
class MyClass {
private:
int* data;
public:
MyClass(int size) {
data = new int[size]; // allocate memory
}
~MyClass() {
delete[] data; // free allocated memory
}
};
2. Resource Release
Destructors release other types of resources, such as file handles, network connections, or database connections, which were acquired during the object's lifetime.
#include <fstream>
class FileHandler {
private:
std::ofstream file;
public:
FileHandler(const std::string& filename) {
file.open(filename);
}
~FileHandler() {
if (file.is_open()) {
file.close(); // close the file if open
}
}
};
3. Logging and Debugging
Destructors can be used to log messages or perform other debugging activities when an object is destroyed. This can help in tracking the program's behavior and identifying potential issues.
#include <iostream>
class Logger {
public:
Logger() {
std::cout << "Logger created." << std::endl;
}
~Logger() {
std::cout << "Logger destroyed." << std::endl;
}
};
4. Smart Pointers
Destructors are integral to the implementation of smart pointers, which manage the lifetime of dynamically allocated objects and ensure proper cleanup when the pointer is no longer needed.
#include <memory>
class Resource {
public:
Resource() {
std::cout << "Resource acquired." << std::endl;
}
~Resource() {
std::cout << "Resource released." << std::endl;
}
};
void useResource() {
std::unique_ptr<Resource> res(new Resource()); // Resource is automatically released
}
5. Preventing Resource Leaks in Exception Handling
Destructors help in preventing resource leaks by ensuring that resources are released even if an exception occurs. This is crucial in exception-safe programming.
class ExceptionSafe {
private:
int* data;
public:
ExceptionSafe(int size) {
data = new int[size];
if (size < 0) {
throw std::runtime_error("Negative size not allowed");
}
}
~ExceptionSafe() {
delete[] data; // will be called even if constructor throws an exception
}
};
6. Custom Cleanup Logic
Destructors can be used to execute custom cleanup logic specific to the needs of the class, such as resetting static variables, unregistering from observers, or performing other cleanup tasks.
class Observer {
private:
static int count;
public:
Observer() {
++count;
}
~Observer() {
--count; // adjust the count when an object is destroyed
}
static int getCount() {
return count;
}
};
int Observer::count = 0;
Our Free Courses with Certificate
Introduction to C++ Enroll Now! Learn Advanced C++ Course Enroll Now! C Programming Basics Enroll Now! Introduction to C# Enroll Now! JavaScript for Beginners Enroll Now!
C++ Destructor Syntax
1. Declaration
- The destructor has the same name as the class, prefixed with a tilde (~).
- It does not take any arguments and does not return a value.
class ClassName {
public:
~ClassName(); // Destructor declaration
};
2. Definition
- The destructor can be defined inside or outside the class definition.
- Inside the class definition:
class ClassName {
public:
~ClassName() {
// Destructor code
}
};
- Outside the class definition:
class ClassName {
public:
~ClassName(); // Destructor declaration
};
ClassName::~ClassName() {
// Destructor code
}
Example
#include <iostream>
class MyClass {
public:
MyClass() {
std::cout << "Constructor called." << std::endl;
}
~MyClass() {
std::cout << "Destructor called." << std::endl;
}
};
int main() {
MyClass obj; // Constructor is called here
// Destructor will be called automatically when obj goes out of scope
return 0;
}
Properties of C++ Destructor
1. Name and Syntax
- A destructor has the same name as the class but is preceded by a tilde (~).
- It does not take any arguments and does not return a value.
- Syntax: ~ClassName();
2. Automatic Invocation
- The destructor is called automatically when an object goes out of scope or is explicitly deleted using the delete keyword.
- You do not need to (and should not) explicitly call a destructor in your code.
3. No Parameters
Destructors cannot take parameters, and there can only be one destructor per class.
4. No Return Type
Destructors do not have a return type, not even void.
5. Virtual Destructors
- If a class intends to be used as a base class, its destructor should be declared virtual. This ensures that the destructor of the derived class is called when an object is deleted through a base class pointer.
- Syntax: virtual ~BaseClass();
6. Order of Destruction
- Destructors are called in the reverse order of the constructors. This means that the destructor of a derived class is called first, followed by the destructors of its base classes.
- Member objects are destroyed after the destructor of the containing object runs.
7. Resource Release
- Destructors are typically used to release resources such as dynamically allocated memory, file handles, network connections, etc.
- Properly implementing destructors is crucial to avoid resource leaks.
8. Non-Inheritance
Destructors are not inherited. Each class has its own destructor, and the derived class destructor does not override the base class destructor. However, declaring the base class destructor as virtual ensures proper cleanup.
When Is a Destructor Called?
A destructor in C++ is called in several situations, each corresponding to different phases in an object's lifecycle. Understanding these scenarios is crucial for effective resource management and ensuring that cleanup operations are performed correctly. Here are the primary situations when a destructor is called:
- When an Object Goes Out of Scope
- When a Dynamically Allocated Object is Deleted
- When an Array of Objects is Deleted
- When a Static Object's Lifetime Ends
- When an Object in a Container is Destroyed
- When an Object is Explicitly Destroyed Using std::unique_ptr or std::shared_ptr
C++ Destructor Overloading
The destructor cannot be overloaded. It is not possible to overwhelm the destructor. In a class, we can only have one destructor. In a class followed by a class name, there can only be one destructor with no parameters and no return type.
Default Destructor and User-Defined C++ Destructor
If we don't write our own destructor in the class, the compiler generates one for us. If we have dynamically allocated memory or a pointer in the class, the default destructor works fine. We can write a destructor to release memory before the class instance is destroyed when a class includes a pointer to memory allocated in the class. To prevent memory leaks, this must be done.
Virtual C++ Destructor
When we have a virtual feature, it is always a good idea to render destructors virtual in the base class. Deleting a derived class object with a non-virtual destructor using a pointer of base class type causes undefined actions. A virtual destructor should be specified in the base class to correct this situation. Following a program, for example, results in undefined actions.
Sample Code For Virtual Destructor
#include <iostream>
class Base {
public:
Base() {
std::cout << "Base constructor called." << std::endl;
}
// Virtual destructor
virtual ~Base() {
std::cout << "Base destructor called." << std::endl;
}
};
class Derived : public Base {
public:
Derived() {
std::cout << "Derived constructor called." << std::endl;
}
~Derived() {
std::cout << "Derived destructor called." << std::endl;
}
};
int main() {
Base* obj = new Derived(); // Polymorphic behavior
delete obj; // Calls both Derived and Base destructors
return 0;
}
Output
The expected output of the program will be:
Base constructor called.
Derived constructor called.
Derived destructor called.
Base destructor called.
Rules for C++ Destructors
According to C++ destructor the statement should begin with a tilde () and the same class name.
- Destructors are not equipped with parameters or a return form.
- Destructors are invoked automatically and cannot be invoked manually from a program.
- Destructors cannot be overloaded.
- Which can be a virtual destructor.
- The Destructor will execute the reverse order of object creation.
- Destructor always present only in the public section.
Accelerate your career as a skilled MERN Stack Developer by enrolling in a unique Full Stack Developer - MERN Stack Master's program. Get complete development and testing knowledge on the latest technologies by opting for the MERN Stack Developer Course. Contact us TODAY!
Conclusion
C++ destructors are class members that remove an object. They are named when the class object is no longer in view, for example, when a method, a program, or a delete variable is called. Destructors vary from member functions in that they do not accept any arguments and do not return anything. Destructors are also given the same name as their class.
If you are interested in 360-degree learning on full stack development, explore Simplilearn’s Full Stack Developer - MERN Stack Masters program. This gives you all the job-ready knowledge and practical training you need to become a professional full-stack developer today.