
- C++ Home
- C++ Overview
- C++ Environment Setup
- C++ Basic Syntax
- C++ Comments
- C++ Hello World
- C++ Omitting Namespace
- C++ Tokens
- C++ Constants/Literals
- C++ Keywords
- C++ Identifiers
- C++ Data Types
- C++ Numeric Data Types
- C++ Character Data Type
- C++ Boolean Data Type
- C++ Variable Types
- C++ Variable Scope
- C++ Multiple Variables
- C++ Input Output Operations
- C++ Basic Input/Output
- C++ Cin
- C++ Cout
- C++ Manipulators
- Type System & Data Representation
- C++ Modifier Types
- C++ Storage Classes
- C++ Constexpr Specifier
- C++ Numbers
- C++ Enumeration
- C++ Enum Class
- C++ References
- C++ Date & Time
- C++ Operators
- C++ Operators
- C++ Arithmetic Operators
- C++ Relational Operators
- C++ Logical Operators
- C++ Bitwise Operators
- C++ Assignment Operators
- C++ sizeof Operator
- C++ Conditional Operator
- C++ Comma Operator
- C++ Member Operators
- C++ Casting Operators
- C++ Pointer Operators
- C++ Operators Precedence
- C++ Unary Operators
- C++ Scope Resolution Operator
- C++ Control Statements
- C++ Decision Making
- C++ if Statement
- C++ if else Statement
- C++ Nested if Statements
- C++ switch Statement
- C++ Nested switch Statements
- C++ Loop Types
- C++ while Loop
- C++ for Loop
- C++ do while Loop
- C++ Foreach Loop
- C++ Nested Loops
- C++ Jump Statements
- C++ break Statement
- C++ continue Statement
- C++ goto Statement
- C++ Return Values
- C++ Strings
- C++ Strings
- C++ Loop Through a String
- C++ String Length
- C++ String Concatenation
- C++ String Comparison
- C++ Functions
- C++ Functions
- C++ Multiple Function Parameters
- C++ Recursive Function
- C++ Function Overloading
- C++ Function Overriding
- C++ Default Arguments
- C++ Arrays
- C++ Arrays
- C++ Multidimensional Arrays
- C++ Pointer to an Array
- C++ Passing Arrays to Functions
- C++ Return Array from Functions
- C++ Array Decay
- C++ Structure & Union
- C++ Structures
- C++ Unions
- C++ Class and Objects
- C++ Object Oriented
- C++ Classes & Objects
- C++ Class Member Functions
- C++ Class Access Modifiers
- C++ Static Class Members
- C++ Static Data Members
- C++ Static Member Function
- C++ Inline Functions
- C++ this Pointer
- C++ Friend Functions
- C++ Pointer to Classes
- C++ Constructors
- C++ Constructor & Destructor
- C++ Default Constructors
- C++ Parameterized Constructors
- C++ Copy Constructor
- C++ Constructor Overloading
- C++ Constructor with Default Arguments
- C++ Delegating Constructors
- C++ Constructor Initialization List
- C++ Dynamic Initialization Using Constructors
- C++ Destructors
- C++ Virtual Destructor
- C++ Inheritance
- C++ Inheritance
- C++ Multiple Inheritance
- C++ Multilevel Inheritance
- C++ Object-oriented
- C++ Overloading
- C++ Polymorphism
- C++ Abstraction
- C++ Encapsulation
- C++ Interfaces
- C++ Virtual Function
- C++ Pure Virtual Functions & Abstract Classes
- C++ Override Specifiers
- C++ Final Specifiers
- C++ Design Patterns
- C++ Creational Design Patterns
- C++ Singleton Design Pattern
- C++ Factory Method Design Pattern
- C++ Abstract Factory Pattern
- C++ Prototype Design Pattern
- C++ Structural Design Patterns
- C++ File Handling
- C++ Files and Streams
- C++ Reading From File
- C++ Advanced
- C++ Exception Handling
- C++ Dynamic Memory
- C++ Namespaces
- C++ Templates
- C++ Preprocessor
- C++ Signal Handling
- C++ Multithreading
- C++ Web Programming
- C++ Socket Programming
- C++ Concurrency
- C++ Advanced Concepts
- C++ Lambda Expression
- C++ nullptr
- C++ unordered_multiset
- C++ Structural Design Patterns
- C++ Adapter Pattern
- C++ Bridge Pattern
- C++ Composite Pattern
- C++ Decorator Pattern
Destructors in C++
A destructor is a special member function of a class that is executed automatically whenever an object of its class goes out of scope or whenever the delete expression is applied to a pointer to the object of that class.
Note: In C++, the static keyword has a different meaning. In this chapter, we're using static memory allocation for stack-based / non-dynamic memory allocation.
The syntax of defining a destructor manually is given below −
~class_name() { // body }
Here's the syntax to define a destructor outside the class, but first we need to declare the destructor in the class −
class_name { public: // Destructor Declaration ~class_name(); } // Defining Destructor class_name :: ~class_name() { // Body }
Defining a Destructor Inside a Class
In this example, we have used the above syntax for defining a destructor manually inside a class. Here, destructor is not needed, we have used destructor just to illustrate how a destructor is defined inside a class −
#include <iostream> using namespace std; class Example{ public: // Constructor called Example(void) { cout << "Constructor is called" << endl; } // Defining destructor inside class ~Example(void) { cout << "Destructor is called" << endl; } }; int main(){ Example ex; return 0; }
The output of the above code is given below −
Constructor is called Destructor is called
Defining a Destructor Outside a Class
The example below demonstrates how to define a destructor(Example) outside the class using scope resolution operator. But first, it needs to be declared inside the class.
#include <iostream> using namespace std; class Example { public: // Constructor called Example(void) { cout << "Constructor is called" << endl; } // Declaring destructor inside class ~Example(); }; // Defining destructor outside class using scope resolution operator Example :: ~Example(void) { cout << "Destructor is called" << endl; } int main(){ Example ex; return 0; }
The output of the above code is given below −
Constructor is called Destructor is called
Why Do We Need Custom Destructors?
C++ provides a default destructor for every class as the classes are created. The default destructor cleans up the statically allocated memory. But, if we have used a pointer for dynamic memory allocation using new, then we need to define a custom destructor with delete operator to clean up the memory.
In this example, the destructor Example1 is just used for displaying the message and not to free the memory as the memory of class Example1 gets freed automatically. But the destructor Example2 is used for freeing the memory using delete operator.
#include <iostream> using namespace std; class Example1 { public: // Constructor called Example1() { cout << "Constructor of Example1 is called" << endl; } // Destructor called ~Example1() { cout << "Destructor of Example1 is called" << endl; } }; class Example2 { private: int *ptr; public: Example2() { ptr = new int; // dynamically allocated memory *ptr = 42; cout << "Constructor of Example2 is called with ptr Value = " << *ptr << endl; } // Destructor called to free dynamically allocated memory ~Example2() { delete ptr; // dynamically allocated memory is freed cout << "Destructor of Example2 is called" << endl; } }; int main(){ Example1 ex1; // object with static memory allocation Example2 ex2; // object with dynamic memory allocation return 0; }
The output of the above code is given below −
Constructor of Example1 is called Constructor of Example2 is called with ptr Value = 42 Destructor of Example2 is called Destructor of Example1 is called
Properties of Destructors in C++
Destructors in C++ have the following properties −
- Destructors are automatically called when an object goes out of scope.
- Destructor has the same name as the class and is denoted using tilde(~) sign.
- It is automatically created when a class is created.
- Destructors do not have any return type and do not accept any parameters/arguments.
- You can not define more than one destructor.
- Destructors cannot be inherited.
- Destructors cannot be overloaded.
Automatic Destructor Call for Statically Allocated Objects
For objects that are created using static memory allocation, destructors are automatically called. Below is an example −
#include <iostream> using namespace std; class MyClass { public: MyClass() { cout << "Constructor called" << endl; } ~MyClass() { cout << "Destructor called automatically" << endl; } }; int main() { cout << "Creating an object using static memory allocation" << endl; { MyClass obj; cout << "Object created" << endl; } // Object goes out of scope here, //destructor called automatically cout << "Object destroyed" << endl; return 0; }
The output of the above code is given below −
Creating an object using static memory allocation Constructor called Object created Destructor called automatically Object destroyed
C++ Destructor for Dynamic Objects
For objects that are created using dynamic memory allocation, you need to manually call the destructor using delete. In the example below, we have manually deleted the object using the delete operator:
#include <iostream> using namespace std; class MyClass { public: MyClass() { cout << "Constructor called" << endl; } ~MyClass() { cout << "Destructor called" << endl; } }; int main() { cout << "Creating an object using dynamic memory allocation:" << endl; // Dynamically creating an object MyClass *obj = new MyClass(); cout << "Object created" << endl; // Manually invoking destructor delete obj; cout << "Object destroyed manually using delete" << endl; return 0; }
The output of the above code is given below −
Creating an object using dynamic memory allocation: Constructor called Object created Destructor called Object destroyed manually using delete
Destructor Call Order for Multiple Objects
Destructors are called in reverse order of construction for multiple objects. In the example below, we can see that the destructors are called in the reverse order −
#include <iostream> using namespace std; class MyClass { private: int id; public: MyClass(int num) : id(num) { cout << "Constructor called for object " << id << endl; } ~MyClass() { cout << "Destructor called for object " << id << endl; } }; int main() { cout << "Creating 3 objects:" << endl; { MyClass obj1(1); // First object MyClass obj2(2); // Second object MyClass obj3(3); // Third object cout << "All objects created" << endl; } // All objects go out of scope here cout << "All objects destroyed" << endl; return 0; }
The output of the above code is given below −
Creating 3 objects: Constructor called for object 1 Constructor called for object 2 Constructor called for object 3 All objects created Destructor called for object 3 Destructor called for object 2 Destructor called for object 1 All objects destroyed
Destructors with Arrays
To clean the memory after an array gets deleted or gets out of scope, we need to call destructor for each array item to avoid any memory leak. There can be three different types of scenarios while using a destructor with an array.
- Using Destructor with Static Array
- Using Destructor with a Dynamic Array
- Using Destructor with an Array of Pointers to Objects
Using a Destructor with a Static Array
In this example, we have a static array of objects, and the memory is freed automatically by the compiler. Here, the destructor is used just for displaying the message.
#include <iostream> using namespace std; class Example { private: int id; public: Example(int i) : id(i) { cout << "Constructor called for object " << id << endl; } ~Example() { cout << "Destructor called for object " << id << endl; } }; int main(){ cout << "Creating array of 3 objects:" << endl; Example arr[3] = {Example(1), Example(2), Example(3)}; cout << "Array created" << endl; return 0; // Destructors called automatically }
The output of the above code is given below −
Creating array of 3 objects: Constructor called for object 1 Constructor called for object 2 Constructor called for object 3 Array created Destructor called for object 3 Destructor called for object 2 Destructor called for object 1
Using a Destructor with a Dynamic Array
In this example, a dynamic array of objects is created using new[] operator. Here, the destructor is used with delete[] operator to clear the memory.
#include <iostream> using namespace std; class Student { private: int rollNo; public: Student(int r = 0) // constructor with default argument { rollNo = r; cout << "Constructor called for roll no: " << rollNo << endl; } ~Student() { cout << "Destructor called for roll no: " << rollNo << endl; } }; int main() { cout << "Creating dynamic array of 3 students:" << endl; Student *students = new Student[3] {101, 102, 103}; // initialize with roll numbers cout << "\nDeleting array:" << endl; delete[] students; // destructor for all 3 objects return 0; }
The output of the above code is given below −
Creating dynamic array of 3 students: Constructor called for roll no: 101 Constructor called for roll no: 102 Constructor called for roll no: 103 Deleting array: Destructor called for roll no: 103 Destructor called for roll no: 102 Destructor called for roll no: 101
Using a Destructor with an Array of Pointers to Objects
In this example, an array of pointers to objects is created. Here, the destructor is used with the delete operator to free the memory of each object individually as each pointer is pointing to a uniquely allocated object.
#include <iostream> using namespace std; class Book { private: string title; public: Book(string t) : title(t) { cout << "Book created: " << title << endl; } ~Book() { cout << "Book destroyed: " << title << endl; } }; int main(){ cout << "Creating array of 3 books:" << endl; Book *library[3]; // Creating individual book objects library[0] = new Book("C++ Basics"); library[1] = new Book("CSS Basics"); library[2] = new Book("Javascript Basics"); cout << "\nDeleting books:" << endl; // Deleting each object individually for (int i = 0; i < 3; i++){ delete library[i]; } return 0; }
The output of the above code is given below −
Creating array of 3 books: Book created: C++ Basics Book created: CSS Basics Book created: Javascript Basics Deleting books: Book destroyed: C++ Basics Book destroyed: CSS Basics Book destroyed: Javascript Basics
Common Mistakes While Working with Destructors
Some of the common beginner mistakes while working with destructors are listed below −
- Using destructor for statically allocated memory. It gets deleted automatically, so there is no need to use a destructor.
- For dynamically allocated memory, forgetting to use the delete.
- In inheritance, not declaring the destructor as virtual in the base class.
- For arrays, use delete[] instead of delete.
- Trying to delete an already-deleted memory twice, also known as double deletion. Set the pointer to nullptr after deleting it, and check the pointer for nullptr before deletion.
Conclusion
Destructors are special member functions to clean up memory when objects go out of scope or program is executed. In C++, there is a default destructor for statically allocated memory, and custom destructors are used for clearing the dynamically allocated memory using the new operator.