07 Constructor and Destructor
07 Constructor and Destructor
Constructors
What are Constructors?
• A constructor is a special member function that is automatically called when an object
of a class is created. Its primary purpose is to initialize the object’s attributes and
allocate resources necessary for the object's operation.
Reasons for Using Constructors:
1. Automatic Initialization: Constructors allow for automatic setup of objects when
they are created, ensuring that they start their life in a valid state.
2. Overloading: Constructors can be overloaded, which means you can have multiple
constructors with different parameters. This provides flexibility in object creation.
3. Encapsulation of Logic: Initialization logic can be encapsulated in constructors,
making the code cleaner and easier to maintain.
4. Resource Management: If your class manages resources dynamically (like memory,
file handles, etc.), constructors can allocate those resources.
5. Constructor Delegating: C++11 introduced the ability to have constructors call other
constructors of the same class, reducing code duplication.
Constructor:
class MyClass {
public:
int x;
// Constructor with one parameter
MyClass(int value) : x(value) {
// Constructor body
}
};
// Usage
MyClass obj(10); // Automatically calls MyClass(int value)
Destructors
What are Destructors?
• A destructor is a special member function that is called when an object goes out of
scope or is explicitly deleted. Its main purpose is to free resources that the object may
have acquired during its lifetime.
Reasons for Using Destructors:
1. Resource Deallocation: Destructors ensure that any resources (memory, file handles,
sockets, etc.) that were allocated during the life of the object are properly released.
2. Prevention of Memory Leaks: If a class allocates memory on the heap, the
destructor can be used to deallocate that memory, thereby preventing memory leaks.
3. Cleanup Logic: Just like constructors can encapsulate initialization logic, destructors
can encapsulate cleanup logic, promoting better resource management practices.
4. Automatic Invocation: Destructors are automatically called when an object’s lifetime
ends, ensuring consistent cleanup without the need for explicit deallocation.
Destructor:
class MyClass {
public:
int* ptr;
// Constructor
MyClass() {
ptr = new int; // Dynamically allocating memory
}
// Destructor
~MyClass() {
delete ptr; // Freeing the allocated memory
}
};
// Usage
{
MyClass obj; // At the end of this scope, the destructor will automatically be called
}
Copy Constructor
In C++, a copy constructor is a special constructor that initializes a new object as a copy of an
existing object. The copy constructor is used when an object is passed by value, returned
from a function, or explicitly constructed from another object. The default copy constructor
provided by the compiler performs a shallow copy, meaning it copies the values of the
member variables. However, if the class allocates dynamic memory, you might want to define
your own copy constructor to perform a deep copy.
• The copy constructor is called when a new object is created from an existing object.
• If a class contains pointers to dynamically allocated resources, a deep copy must be
implemented; otherwise, you can end up with multiple objects sharing the same
memory space, leading to resource management issues like memory leaks and double
deletions.
• Always define a destructor when managing dynamic memory in a class.
Simple Copy Constructor:
We define a simple class Point that represents a point in 2D space. The copy constructor will
handle copying the member variables.
#include <iostream>
class Point {
private:
int x, y;
public:
// Constructor
Point(int xVal, int yVal) : x(xVal), y(yVal) {}
// Copy Constructor
Point(const Point& p) {
x = p.x;
y = p.y;
std::cout << "Copy Constructor called!" << std::endl;
}
int main() {
Point p1(10, 20); // Default Constructor
Point p2 = p1; // Copy Constructor is called
p1.display();
p2.display();
return 0;
}
Output:
Copy Constructor called!
Point(10, 20)
Point(10, 20)
Copy Constructor with Dynamic Memory:
We create a class String that manages a dynamically allocated character array. Here, we need
to ensure that a deep copy is made in the copy constructor to avoid multiple objects pointing
to the same memory.
#include <iostream>
#include <cstring>
class String {
private:
char* str;
public:
// Constructor
String(const char* s) {
str = new char[strlen(s) + 1];
strcpy(str, s);
}
// Destructor
~String() {
delete[] str; // Free the allocated memory
}
return 0;
}
Output:
Dynamic Constructor
In C++, a dynamic constructor is a constructor that allocates memory dynamically for an
object during its execution using the new keyword. This is particularly useful when the size
of the data structure cannot be determined at compile time.
Here are two examples that illustrate the use of dynamic constructors in C++.
Dynamic Array using a Constructor:
We will create a class that represents a dynamic array. The constructor will take the size of
the array as an argument and allocate memory for it dynamically.
#include <iostream>
class DynamicArray {
private:
int* arr;
int size;
public:
// Dynamic constructor
DynamicArray(int s) {
size = s;
arr = new int[size]; // Allocate memory for the array
}
}
std::cout << std::endl;
}
int main() {
DynamicArray myArray(5); // Create a dynamic array of size 5
myArray.setValues(); // Set values in the array
myArray.display(); // Display values
return 0;
}
Output:
12345
Class with Dynamic Memory Allocation for Objects:
We will create a class Person and use a dynamic constructor to allocate memory for a string
that holds the person's name.
#include <iostream>
#include <cstring>
class Person {
private:
char* name;
public:
// Dynamic constructor
Person(const char* n) {
name = new char[strlen(n) + 1]; // Allocate memory for the name
int main() {
Person p1("Alice"); // Create an object with dynamic memory allocation
p1.display(); // Display the name
return 0;
}
Output:
Name: Alice
Explanation
1. In the First Program, we created a DynamicArray class that dynamically allocates an
array based on the size provided. The destructor is used to free the allocated memory.
2. In the second Program, we created a Person class that dynamically allocates memory
for a string to store a person's name. Again, a destructor is used to clean up the
memory.
Explicit Constructor:
In C++, an explicit constructor is one that is declared with the explicit keyword. This
keyword prevents the compiler from using the constructor for implicit type
conversions, which can help avoid unexpected behaviors in your code.
Here's a brief explanation followed by two examples.
When to Use explicit
• Implicit conversions can lead to unexpected results or bugs, especially when
performing assignments or function calls.
• By marking a constructor as explicit, you ensure that it can only be explicitly called
(i.e., with a direct object creation syntax).
Basic Usage of Explicit Constructor:
#include <iostream>
class Box {
public:
// Explicit constructor
explicit Box(int length) : length(length) {}
private:
int length;
};
int main() {
Box box1(3); // Direct initialization (OK)
std::cout << "Volume of box1: " << box1.getVolume() << std::endl;
return 0;
}
the Box class has an explicit constructor. Trying to create a Box using implicit
conversion (e.g., Box box2 = 5;) would result in a compilation error. Instead, you
must explicitly create a Box object like Box box2 = Box(5);.
Preventing Implicit Conversion:
#include <iostream>
class Fraction {
public:
// Explicit constructor
explicit Fraction(int numerator) : numerator(numerator), denominator(1) {}
explicitly.
Destructor:
In C++, a destructor is a special member function that is invoked when an object of a
class goes out of scope or is explicitly deleted. The main purpose of a destructor is to
release resources that the object may have acquired during its lifetime, such as
dynamically allocated memory, file handles, etc. A destructor has the same name as
the class but is prefixed with a tilde (~). It does not take any parameters and does not
return a value.
1. Automatic Call: Destructors are called automatically when an object's lifetime ends
(when it goes out of scope or is explicitly deleted).
2. No Parameters or Return Type: Destructors do not take parameters and do not
return a value.
3. Single Destructor: A class can only have one destructor.
4. Resource Management: Destructors are essential for resource management,
especially when dealing with dynamic memory, file descriptors, and other resources
that require explicit cleanup.
Basic Destructor:
We will demonstrate a destructor that releases dynamically allocated memory.
#include <iostream>
class MyClass {
private:
int* data;
public:
// Constructor to allocate memory
MyClass(int size) {
data = new int[size]; // dynamically allocate array of integers
std::cout << "Memory allocated for " << size << " integers." << std::endl;
}
// Destructor to free the allocated memory
~MyClass() {
delete[] data; // release allocated memory
std::cout << "Memory freed." << std::endl;
}
};
int main() {
MyClass obj(5); // Create an object of MyClass
// Memory is allocated for 5 integers
// The destructor will be called automatically when obj goes out of scope
return 0;
}
Output:
Memory allocated for 5 integers.
Memory freed.
Destructor with File Handling:
In this program destructor that closes an open file when the object goes out of scope.
#include <iostream>
#include <fstream>
class FileHandler {
private:
std::fstream file;
public:
// Constructor opens a file
FileHandler(const std::string& filename) {
file.open(filename, std::ios::out | std::ios::app);
if (file.is_open()) {
std::cout << "File opened: " << filename << std::endl;
} else {
std::cerr << "Error opening file: " << filename << std::endl;
}
}
// Destructor closes the file
~FileHandler() {
if (file.is_open()) {
file.close();
std::cout << "File closed." << std::endl;
}
}
// Example method to write to the file
void writeToFile(const std::string& data) {
if (file.is_open()) {
file << data << std::endl;
}
}
};
int main() {
{
FileHandler fh("example.txt");
fh.writeToFile("Hello, World!");
} // FileHandler goes out of scope here and the destructor is called
private:
static int count; // Static member to keep track of count
public:
// Constructor increments the count
Counter() {
count++;
std::cout << "Constructor called. Current count: " << count << std::endl;
}
// Destructor decrements the count
~Counter() {
count--;
std::cout << "Destructor called. Current count: " << count << std::endl;
}
// Static function to get the current count
static int getCount() {
return count;
}
};
~User() {
std::cout << "User with ID: " << userID << " is being destroyed." << std::endl;
}
// Function to get the current ID count
static int getIdCount() {
return idCounter;
}
};
// Define and initialize the static member
int User::idCounter = 0;
int main() {
std::cout << "Creating users..." << std::endl;
User user1;
User user2;
{
User user3; // This goes out of scope here
}
std::cout << "Total users created: " << User::getIdCount() << std::endl;
return 0; // user1 and user2 go out of scope here
}
Output:
• Every time a User object is created, the constructor increments idCounter and assigns
the new value to the instance's userID.
• The destructor prints a message when a user object is destroyed, showing which user
is being destroyed.