Introduction to C++ | Destructor
C++ Destructor
A destructor in C++ is a special member function that is automatically invoked when an object goes out of scope or is explicitly deleted. The purpose of a destructor is to clean up resources acquired by an object during its lifetime, such as releasing dynamically allocated memory or closing file handles. A destructor ensures proper cleanup and helps prevent memory leaks and other resource management issues.
What is a Destructor?
A destructor has the following characteristics:
- It is a member function with the same name as the class but preceded by a tilde (~).
- It is invoked automatically when an object goes out of scope or is explicitly deleted.
- It does not take any arguments and does not return any value.
- A class can have only one destructor.
The main function of a destructor is to release resources such as memory or file handles that were acquired by the object during its lifetime.
Syntax of Destructor
The syntax for declaring a destructor is as follows:
Syntax of Destructor
class ClassName {
public:
~ClassName() {
// Destructor body: Cleanup code here
}
};
Example of Destructor
Here’s a simple example of how a destructor works in C++:
Code Example: Destructor
#include <iostream>
using namespace std;
class Car {
public:
string brand;
int year;
// Constructor to initialize brand and year
Car(string b, int y) {
brand = b;
year = y;
cout << "Car " << brand << " created!" << endl;
}
// Destructor to clean up resources
~Car() {
cout << "Car " << brand << " destroyed!" << endl;
}
void displayDetails() {
cout << "Brand: " << brand << ", Year: " << year << endl;
}
};
int main() {
// Creating an object of class Car
Car myCar("Toyota", 2020);
myCar.displayDetails(); // Output: Brand: Toyota, Year: 2020
return 0; // Destructor is called when myCar goes out of scope
}
Output:
Brand: Toyota, Year: 2020
Car Toyota destroyed!
When is a Destructor Called?
A destructor is called in the following situations:
- When an object goes out of scope (for local objects).
- When the
delete
operator is used to deallocate memory for a dynamically allocated object.
For example, when a function exits, any local variables in the function will go out of scope, and their destructors will be automatically called.
Example of Destructor with Dynamic Memory Allocation
If a class allocates memory dynamically, the destructor is responsible for deallocating that memory to avoid memory leaks. Here's an example of a destructor that handles dynamic memory allocation:
Code Example: Destructor with Dynamic Memory
#include <iostream>
using namespace std;
class Car {
public:
string* brand;
int year;
// Constructor with dynamic memory allocation
Car(string b, int y) {
brand = new string(b); // Allocate memory dynamically
year = y;
}
// Destructor to free dynamically allocated memory
~Car() {
delete brand; // Free the allocated memory
}
void displayDetails() {
cout << "Brand: " << *brand << ", Year: " << year << endl;
}
};
int main() {
// Creating an object of class Car
Car myCar("Honda", 2022);
myCar.displayDetails(); // Output: Brand: Honda, Year: 2022
return 0; // Destructor is called here to free dynamically allocated memory
}
Output:
Destructor in Inheritance
In C++, destructors in derived classes are automatically called when a base class object is destroyed. However, if the base class has a destructor that is not virtual, it may not properly invoke the destructor of the derived class, which can lead to resource leaks. To ensure proper cleanup in case of inheritance, it's good practice to declare the destructor in the base class as virtual.
Example of Virtual Destructor in Inheritance
Here’s an example where a virtual destructor ensures proper cleanup in a class hierarchy:
Code Example: Virtual Destructor
#include <iostream>
using namespace std;
class Vehicle {
public:
virtual ~Vehicle() { // Virtual destructor
cout << "Vehicle destroyed!" << endl;
}
};
class Car : public Vehicle {
public:
~Car() {
cout << "Car destroyed!" << endl;
}
};
int main() {
Vehicle* vehicle = new Car(); // Create a Car object using base class pointer
delete vehicle; // Calls the destructor of Car, followed by Vehicle
return 0;
}
Output:
Vehicle destroyed!
Pro Tip:
💡 Pro Tip
Always define destructors as virtual in base classes when dealing with inheritance. This ensures that the derived class’s destructor is called when an object is deleted through a base class pointer, preventing potential memory leaks and resource issues.