What is a Constructor?
A constructor is a special member function that is called automatically when an object is created. It has the same name as the class and is used to initialize the object’s data members.
Purpose of Constructors
The main purpose of constructors is to ensure that objects start their life in a valid and consistent state by:
- Initializing data members
- Allocating resources needed by the object
- Setting up the object’s initial state
Rules for Constructors
- Constructor name must be the same as the class name
- Constructors don’t have a return type (not even void)
- Constructors are automatically called when objects are created
- A class can have multiple constructors (constructor overloading)
- If no constructor is defined, the compiler provides a default one
Default Constructor
A default constructor is one that takes no arguments. There are two kinds:
- Compiler-provided default constructor: Created automatically if you don’t define any constructor
- User-defined default constructor: Created by the programmer with no parameters
Compiler-provided Default Constructor
class Box {
private:
double length; // Will be initialized with garbage value
double width; // Will be initialized with garbage value
public:
void setDimensions(double l, double w) {
length = l;
width = w;
}
};
int main() {
Box b; // Compiler's default constructor called
b.setDimensions(5, 3);
return 0;
}
User-defined Default Constructor
class Box {
private:
double length;
double width;
public:
// User-defined default constructor
Box() {
length = 1.0; // Default value
width = 1.0; // Default value
cout << "Default constructor called" << endl;
}
};
int main() {
Box b; // User's default constructor called
return 0;
}
Parameterized Constructor
A parameterized constructor takes one or more parameters to initialize an object with specific values.
class Student {
private:
string name;
int rollNumber;
public:
// Parameterized constructor
Student(string n, int r) {
name = n;
rollNumber = r;
}
void display() {
cout << "Name: " << name << ", Roll: " << rollNumber << endl;
}
};
int main() {
// Using parameterized constructor
Student s1("John", 101);
Student s2("Alice", 102);
s1.display(); // Output: Name: John, Roll: 101
s2.display(); // Output: Name: Alice, Roll: 102
return 0;
}
Constructor with Default Arguments
You can also create constructors with default argument values:
class Rectangle {
private:
double length;
double width;
public:
// Constructor with default arguments
Rectangle(double l = 1.0, double w = 1.0) {
length = l;
width = w;
}
double area() {
return length * width;
}
};
int main() {
Rectangle r1; // Uses default values: 1.0, 1.0
Rectangle r2(5.0); // Uses: 5.0, 1.0
Rectangle r3(5.0, 3.0); // Uses: 5.0, 3.0
cout << "r1 area: " << r1.area() << endl; // Output: 1.0
cout << "r2 area: " << r2.area() << endl; // Output: 5.0
cout << "r3 area: " << r3.area() << endl; // Output: 15.0
return 0;
}
Copy Constructor
A copy constructor creates a new object as a copy of an existing object. It takes a reference to an object of the same class as a parameter.
class Point {
private:
int x, y;
public:
// Regular constructor
Point(int a = 0, int b = 0) {
x = a;
y = b;
cout << "Regular constructor called" << endl;
}
// Copy constructor
Point(const Point &p) {
x = p.x;
y = p.y;
cout << "Copy constructor called" << endl;
}
void display() {
cout << "Point: (" << x << ", " << y << ")" << endl;
}
};
When is the Copy Constructor Called?
The copy constructor is called in the following situations:
-
When an object is created from another object:
Point p1(10, 20); // Regular constructor Point p2 = p1; // Copy constructor Point p3(p1); // Copy constructor -
When an object is passed by value to a function:
void showPoint(Point p) { // Copy constructor called for parameter p.display(); } Point p1(10, 20); showPoint(p1); // Copy constructor called here -
When a function returns an object by value:
Point createPoint() { Point temp(5, 10); return temp; // Copy constructor may be called here } Point p = createPoint(); // May invoke copy constructor
Compiler-provided Copy Constructor
If you don’t define a copy constructor, the compiler provides one that performs a member-by-member copy (shallow copy).
class Student {
private:
string name;
int rollNumber;
public:
Student(string n, int r) {
name = n;
rollNumber = r;
}
// No copy constructor defined - compiler will provide one
void display() {
cout << "Name: " << name << ", Roll: " << rollNumber << endl;
}
};
int main() {
Student s1("John", 101);
Student s2 = s1; // Compiler's copy constructor used
s2.display(); // Output: Name: John, Roll: 101
return 0;
}
Deep Copy vs Shallow Copy
-
Shallow Copy (default by compiler): Copies all member values as-is. If members are pointers, only the pointer addresses are copied, not the data they point to.
-
Deep Copy (requires custom copy constructor): Creates new memory for pointer data and copies the values, not just the addresses.
class DeepCopyExample {
private:
int* data;
public:
// Regular constructor
DeepCopyExample(int value) {
data = new int; // Allocate memory
*data = value;
}
// Deep copy constructor
DeepCopyExample(const DeepCopyExample &source) {
data = new int; // Allocate new memory
*data = *(source.data); // Copy the value
}
// Destructor
~DeepCopyExample() {
delete data; // Free memory
}
void setValue(int value) {
*data = value;
}
void display() {
cout << "Value: " << *data << endl;
}
};
int main() {
DeepCopyExample obj1(10);
DeepCopyExample obj2 = obj1; // Deep copy constructor called
obj1.display(); // Output: Value: 10
obj2.display(); // Output: Value: 10
// Change obj2's data
obj2.setValue(20);
obj1.display(); // Output: Value: 10 (unchanged)
obj2.display(); // Output: Value: 20 (changed)
return 0;
}
Constructor Overloading
Constructor overloading allows a class to have multiple constructors with different parameter lists.
class Box {
private:
double length, width, height;
public:
// Default constructor
Box() {
length = width = height = 1.0;
}
// Constructor with one parameter
Box(double side) {
length = width = height = side;
}
// Constructor with three parameters
Box(double l, double w, double h) {
length = l;
width = w;
height = h;
}
double volume() {
return length * width * height;
}
};
int main() {
Box box1; // Uses default constructor
Box box2(5.0); // Uses constructor with one parameter
Box box3(2.0, 3.0, 4.0); // Uses constructor with three parameters
cout << "Volume of box1: " << box1.volume() << endl; // Output: 1
cout << "Volume of box2: " << box2.volume() << endl; // Output: 125
cout << "Volume of box3: " << box3.volume() << endl; // Output: 24
return 0;
}
Initialization Lists
A more efficient way to initialize class members is to use an initialization list:
class Person {
private:
string name;
int age;
public:
// Using initialization list
Person(string n, int a) : name(n), age(a) {
cout << "Person created" << endl;
}
void display() {
cout << "Name: " << name << ", Age: " << age << endl;
}
};
Initialization lists are especially important for:
- Const members (which must be initialized)
- Reference members (which must be initialized)
- Members without default constructors
- Better performance in some cases
Common Mistakes and Pitfalls
- Forgetting to initialize all members: Some members may have garbage values
- Redundant code in constructors: Duplicate initialization logic across constructors
- Not providing a default constructor: May cause issues when creating arrays or using containers
- Not handling dynamic memory properly: May lead to memory leaks
Summary
Constructors are special member functions that initialize objects when they are created. They ensure that objects start with a valid state. C++ supports different types of constructors: default constructors, parameterized constructors, and copy constructors. Understanding how to properly use constructors is essential for creating well-designed and robust C++ classes.