Destructor in c++

In C++, a destructor is a special member function of a class that is responsible for cleaning up resources and performing necessary actions before an object is destroyed or goes out of scope. The destructor is automatically called when an object is no longer needed, either because it is explicitly deleted or because it goes out of scope.

The primary purpose of a destructor is to release any resources acquired by the object during its lifetime. This may include freeing dynamically allocated memory, closing files, releasing network connections, or performing any other cleanup operations.


The syntax for a destructor is similar to that of a constructor, but it is preceded by a tilde (~). Here's the general form:

class ClassName {
public:
    // Constructor
    ClassName() {
        // Constructor code
    }

    // Destructor
    ~ClassName() {
        // Destructor code
    }
};

The destructor is automatically invoked when an object of the class is destroyed. For example:
{
    ClassName obj; // Object created
} // Object goes out of scope, destructor called

It's important to note that if a class doesn't define its own destructor, C++ provides a default destructor. However, the default destructor may not be sufficient for classes that manage dynamic resources, and in such cases, a custom destructor should be implemented to ensure proper cleanup.



Here's an example of a class with a custom destructor:

#include <iostream>

class ResourceHolder {
public:
    // Constructor
    ResourceHolder() {
        std::cout << "Constructor: Resource acquired\n";
    }

    // Destructor
    ~ResourceHolder() {
        std::cout << "Destructor: Cleaning up resources\n";
        // Code to release resources (e.g., free memory, close files, etc.)
    }
};

int main() {
    ResourceHolder obj; // Constructor called

    // Do some work with obj

    // obj goes out of scope, destructor called automatically
    return 0;
}


Why are destructors important in C++? [Key Points]:


  • Resource Deallocation : Destructors release allocated resources, like memory or file handles, ensuring efficient resource management in an object's lifecycle.
  • Preventing Resource Leaks : Destructors prevent memory leaks by automatically releasing resources when an object goes out of scope, promoting clean and leak-free code.
  • Orderly Cleanup : Destructors enable developers to specify the order in which resources are released upon an object's destruction, crucial for maintaining proper functionality.
  • Automatic Cleanup : Automatic invocation of destructors ensures seamless resource cleanup, reducing the risk of errors in resource management and enhancing code robustness.
  • Exception Safety : Destructors contribute to exception safety by guaranteeing resource release, even in the presence of exceptions, maintaining program integrity.
  • Resource Management in Custom Classes : Destructors allow controlled release of resources in custom classes, such as dynamic memory or file handles, ensuring proper cleanup.
  • RAII (Resource Acquisition Is Initialization) Principle : Destructors play a key role in the RAII principle, linking resource acquisition and release to object lifetime for simplified and error-resistant resource management.
  • Custom Cleanup Logic : Destructors enable the definition of custom cleanup logic, empowering developers to implement specific actions when an object is destroyed, adding flexibility to class behaviors.


Types of Destructors



1. Default Destructor:

If a class does not have a user-defined destructor, the compiler provides a default destructor. It has an empty body and is automatically called when an object goes out of scope or is explicitly deleted.

class MyClass {
    // No user-defined destructor
};

2. User-Defined Destructor:

A user-defined destructor is explicitly declared and defined by the programmer. It performs specific cleanup operations when an object is destroyed. It replaces the default destructor if provided.

            class MyClass {
            public:
                // Constructor (if needed)
                MyClass() {
                    // Initialization code
                }

                // Other class members...

                // User-defined destructor
                ~MyClass() {
                    // Cleanup code (e.g., releasing resources)
                }
            };
        


Explaining the order in which destructors are called.


  1. Destructors are called in the reverse order of construction.
  2. Follows the Last In, First Out (LIFO) principle.
  3. Local (automatic) objects have destructors called when going out of scope.
  4. Static objects have destructors called in reverse order during program termination.
  5. Dynamic objects (allocated with new) have destructors called explicitly with delete.
  6. Inheritance hierarchy: Destructors called from most derived class to base classes.
  7. Important for managing resources and ensuring correct cleanup sequence.


here we have c++ program that demonstrates how many number of times object created that much number of time constructors and destructors are called and also describe destructor move in reverse order to constructor.

#include <iostream>
using namespace std;

class Test {
private:
    static int count; // Static member variable to count objects

public:
    // Constructor
    Test() {
        count++;
        cout << "No. of Objects created: " << count << endl;
    }

    // Destructor
    ~Test() {
        cout << "No. of Objects destroyed: " << count << endl;
        count--;
    }
};

int Test::count = 0; // Initializing static member variable

// Driver code
int main() {
    Test t1, t2, t3;

    return 0;
}
Output:
No. of Objects created: 1
No. of Objects created: 2
No. of Objects created: 3

No. of Objects destroyed: 3
No. of Objects destroyed: 2
No. of Objects destroyed: 1


Here we have a program which will discribe the scope of destructor execution.

#include <iostream>
using namespace std;
class MyClass
{
public:
	MyClass()
	{
		cout << "Constructor called  Object address: " << this << endl;
	}

	~MyClass()
	{
		cout << "Destructor called Object address: " <<this << endl;
	}
};

int main()
{
	cout << endl<< endl << "Creating object obj1" << endl<< endl;
	MyClass obj1; // Constructor called
	
	{
	cout << endl<< endl<< "Creating object obj2" << endl<< endl;
	MyClass obj2; // Constructor called
	}// Destructor called for obj2 when it goes out of scope
	
	cout << endl<< endl<< "Creating object obj3" << endl<< endl;
	MyClass obj3; // Constructor called
	
	return 0;
} // Destructor called for obj1 when main() exits
Output:
Creating object obj1

Constructor called  Object address: 0x6ffe0f


Creating object obj2

Constructor called  Object address: 0x6ffe0d
Destructor called Object address: 0x6ffe0d


Creating object obj3

Constructor called  Object address: 0x6ffe0e
Destructor called Object address: 0x6ffe0e

Destructor called Object address: 0x6ffe0f


How to call destructors explicitly?

In C++, destructors are automatically called when an object goes out of scope or is explicitly deleted. However, you cannot directly call a destructor like a regular member function. Instead, you can destroy an object explicitly using the delete keyword, which triggers the destructor call. Here's a very simple example:

#include 
using namespace std;

class MyClass {
public:
    ~MyClass() {
        cout << "Destructor called" << endl;
    }
};

int main() {
    MyClass* obj = new MyClass(); // Creating object dynamically

    // Deleting object explicitly, which calls the destructor
    delete obj;

    return 0;
}

Destructor vs. Delete Operator



Aspect Destructor Delete Operator
Definition A special member function in a class that is automatically called when an object goes out of scope or is explicitly deleted. An operator in C++ used to deallocate memory that was previously allocated using the new operator.
Usage Part of the class definition and is automatically invoked when an object's lifetime ends. Used explicitly to free the memory allocated dynamically using new.
Memory Deallocation Primarily used for releasing resources, closing files, or any cleanup operations within the class. Specifically used for deallocating memory previously allocated on the heap with new.
Order of Execution Called automatically in the reverse order of object construction during stack unwinding. Not related to the order of object construction; manually invoked when needed.
Scope Applies to objects with automatic or dynamic storage duration. Primarily used for objects with dynamic storage duration (allocated on the heap).


Dynamic Memory vs Destructors


Aspect Dynamic Memory Destructors
Allocation Allocated using operators like `new` or `malloc` on the heap. Automatic memory allocation occurs when objects are created, with destructors responsible for cleanup.
Deallocation Manual deallocation using operators like `delete` or `free` is required to avoid memory leaks. Automatic deallocation occurs when objects go out of scope or are explicitly deleted, handled by the destructor.
Lifetime Objects have a dynamic lifetime, persisting until explicitly deallocated. Objects have a scoped or dynamic lifetime, with destructors managing cleanup during their destruction.
Responsibility The programmer is responsible for both allocation and deallocation of memory. Destructors are responsible for releasing resources, including dynamic memory, when objects are destroyed.
Resource Management Requires careful management to avoid memory leaks or dangling pointers. Provides a structured approach to resource management, enhancing code reliability and robustness.
Exceptions Memory leaks may occur if deallocation is not performed correctly. Destructors contribute to exception safety, ensuring cleanup even in the presence of exceptions.


Destructor In Case Of Inheritance


Aspect Base Class Derived Class
Definition May have a user-defined or default destructor. May have a user-defined or default destructor.
Order of Execution Base class destructor is called first during destruction. Derived class destructor is called after the base class destructor.
User-Defined Destructor Can contain custom cleanup logic for the base class. Can contain custom cleanup logic for the derived class in addition to the base class.
Default Destructor Used if no user-defined destructor is provided. Used if no user-defined destructor is provided; calls the base class default destructor.
Implicit Call Automatically called when an object of the base class is destroyed. Automatically calls the base class destructor before executing its own destructor.
Explicit Call Can be explicitly called by the derived class, but this is less common. Can be explicitly called by the derived class, but this is less common. Generally, the base class destructor is implicitly called.