Constructors and Destructors in C++
Learn constructors and destructors in C++ to understand object lifetime, initialization, and resource management.
In C++, every class has special methods that are called when an object is created or destroyed: Constructor and Destructor. The constructor prepares the object for use, while the destructor cleans up memory. This structure forms the foundation of the RAII (Resource Acquisition Is Initialization) principle.
1. What Is a Constructor?
A constructor is a special method that is automatically called when an object of a class is created. Its purpose is to initialize variables and prepare the necessary resources.
- Its name must be the same as the class name.
- It has no return type.
- It can be overloaded (multiple constructors in the same class).
class Book {
private:
string title;
int pages;
public:
// Default constructor
Book() {
title = "Unknown";
pages = 0;
cout << "Default constructor called." << endl;
}
// Parameterized constructor
Book(string t, int p) {
title = t;
pages = p;
cout << "Parameterized constructor called." << endl;
}
void Display() {
cout << "Book: " << title << " (" << pages << " pages)" << endl;
}
};
The constructor runs automatically when the object is created:
int main() {
Book b1;
Book b2("C++ Programming", 900);
b1.Display();
b2.Display();
}
2. What Is a Destructor?
A destructor is a special method that is called when an object’s lifetime ends. It is used to perform cleanup operations such as freeing memory or closing files.
- It has the same name as the class, but is preceded by a
~(tilde). - It takes no parameters and cannot be overloaded.
class Book {
public:
Book() {
cout << "Constructor executed." << endl;
}
~Book() {
cout << "Destructor executed." << endl;
}
};
int main() {
Book b;
} // Destructor is automatically called at the end of main
Output:
Constructor executed.
Destructor executed.
Since the destructor is called automatically, it should not be invoked manually.
3. Book Example: Extended Constructor and Destructor
Now let’s make the Book class more realistic. It will contain dynamically allocated data (for example, a book description). This will show why destructors are important.
#include <iostream>
#include <string>
using namespace std;
class Book {
private:
string title;
int pages;
string *description; // Dynamic memory
public:
// Constructor
Book(string t, int p, string d)
: title(t), pages(p) {
description = new string(d);
cout << "Book constructor: " << title << endl;
}
// Destructor
~Book() {
cout << "Book destructor: " << title << endl;
delete description; // Free dynamic memory
}
void Display() const {
cout << "Book: " << title << " (" << pages << " pages)" << endl;
cout << "Description: " << *description << endl;
}
};
int main() {
Book b1("1984", 328, "A dystopian novel.");
b1.Display();
}
Output:
Book constructor: 1984
Book: 1984 (328 pages)
Description: A dystopian novel.
Book destructor: 1984
The destructor is called automatically when the object in main() goes out of scope
and releases the memory allocated with new.
4. Constructor Overloading
Multiple constructors can be defined in the same class. The appropriate one is called based on the number and type of parameters.
class Book {
private:
string title;
int pages;
public:
Book() : title("Unknown"), pages(0) {}
Book(string t) : title(t), pages(100) {}
Book(string t, int p) : title(t), pages(p) {}
void Display() {
cout << "Book: " << title << " (" << pages << " pages)" << endl;
}
};
int main() {
Book b1;
Book b2("C++ 101");
Book b3("Design Patterns", 450);
b1.Display();
b2.Display();
b3.Display();
}
5. Copy Constructor
By default, C++ automatically creates a copy constructor, but if your class uses dynamic memory, you must define your own copy constructor.
class Book {
private:
string title;
string *desc;
public:
Book(string t, string d) {
title = t;
desc = new string(d);
cout << "Constructor: " << title << endl;
}
// Copy constructor
Book(const Book &other) {
title = other.title;
desc = new string(*other.desc); // deep copy
cout << "Copy constructor: " << title << endl;
}
~Book() {
delete desc;
cout << "Destructor: " << title << endl;
}
void Display() {
cout << title << " - " << *desc << endl;
}
};
int main() {
Book b1("C++ Primer", "Beginner's guide");
Book b2 = b1; // copy constructor is called
b2.Display();
}
If a deep copy is not performed, both objects will share the same memory, leading to a “double delete” error when the destructor is called.
6. Book System – Lifecycle Tracking
The following example shows the creation and destruction order of a book object. It’s ideal for understanding the actual lifecycle of constructors and destructors.
#include <iostream>
#include <string>
using namespace std;
class Author {
public:
Author(string n) { cout << "Author created: " << n << endl; }
~Author() { cout << "Author destroyed." << endl; }
};
class Book {
private:
string title;
Author author;
public:
Book(string t, string a) : title(t), author(a) {
cout << "Book created: " << title << endl;
}
~Book() {
cout << "Book destroyed: " << title << endl;
}
};
int main() {
Book b("Animal Farm", "George Orwell");
}
Output:
Author created: George Orwell
Book created: Animal Farm
Book destroyed: Animal Farm
Author destroyed.
As you can see, the Author constructor is called first,
followed by the Book constructor.
During destruction, the reverse order occurs (Book first, then Author).
This demonstrates the lifecycle order in composition relationships in C++.
7. TL;DR
- Constructor → Automatically called when an object is created.
- Destructor → Automatically called when an object’s lifecycle ends.
- Constructors can be overloaded, destructors cannot.
- If dynamic memory is used, the destructor must call
delete. - The copy constructor should perform a deep copy.
- Creation order: first subcomponents, then the class; Destruction order: first the class, then subcomponents.
- All examples can be run in Visual Studio 2022 or GCC 11+ compilers.