Search⌘ K
AI Features

Constructors

Explore how constructors work in C++ classes, including their types such as default, parameterized, and copy constructors. Understand how to initialize objects properly, use initializer lists, and the importance of deep copying versus shallow copying in class design.

Introduction

Constructors are special member functions of a class that are automatically called when an object of the class is created.

  • A constructor is a member function that is usually public.

  • A constructor can be used to initialize member variables when an object is created.

A constructor’s name must be the same as the name of the class it is declared in. A constructor cannot return a value. So, no return type, not even void can be used while declaring a constructor. If no constructor is declared, C++ will generate a default constructor that initializes data members to default values.

Declaration and initialization

Constructors are mostly public. We can declare and initialize the constructor within the class or declare it inside the class and write the method outside the class.

A constructor​ for the DayofYear class can be declared as follows:

C++
class DayOfYear {
public:
DayOfYear() // Constructor
{
// Body of constructor
}
private:
int month;
int day;
};

Unlike normal member functions of a class, a constructor can’t be called by using . operator or -> operator. Constructors are automatically called when we create an object of a class.

Types of constructors

There are three different types of constructors:

  • Default constructor

  • Parameterized constructor

  • Copy constructor

Default constructor

A default constructor either takes no parameters or has default arguments for all parameters. It initializes the object with default values. It’s like a default initialization process for the object. Here’s an example default constructor:

C++
class DayOfYear {
public:
DayOfYear() // Constructor
{
month = 1; // Default values
day =1; // Default values
}
private:
int month;
int day;
};

Here, we assign default values 1 to the data members, i.e., month and day of DayOfYear class.

Note: It’s a good practice to use default constructors even if you don’t want to initialize any variables.

Parameterized constructor

A parameterized constructor takes one or more arguments and allows us to initialize objects with specific values.

C++
class DayOfYear {
public:
DayOfYear(int new_month, int new_day)
{
month = new_month;
day = new_day;
}
private:
int month;
int day;
};

In the above code, the constructor for the DayOfYear class takes two parameters new_month and new_day and then we use these parameters to set the values of data members of the class. Just like functions, a constructor can also have default parameter values. This allows us to call the constructor with fewer arguments than the number of parameters defined. If we do not provide a value for a parameter, the default value is used.

Initializer lists

Sometimes, instead of assigning values inside the constructor body, we initialize data members directly after the constructor parameters. This is called an initializer list.

An initializer list begins with a colon : after the constructor declaration, followed by the data members and the values used to initialize them.

class DayOfYear {
public:
DayOfYear(int new_month, int new_day)
: month(new_month), day(new_day)
{
}
private:
int month;
int day;
};

In this example, month and day are initialized directly using the values passed to the constructor.

This approach is often cleaner than assigning values inside the constructor body, especially as classes become larger.

Note: Initializer lists are required when initializing const data members, reference members, or member objects that do not have a default constructor.

Copy constructor

A copy constructor initializes an object as a copy of another existing object of the same class. It takes a reference to an object of the same class as an argument. It is used when an object is passed by value, returned from a function, or explicitly copied.

For example, we can have a copy constructor like this:

C++
class DayOfYear {
public:
DayOfYear(const DayOfYear &obj)
{
month = obj.month;
day = obj.day;
}
private:
int month;
int day;
};

In the above code, the & in the copy constructor indicates that the object is passed by referencePassed by reference means we pass the object's memory address instead of a copy, which allows us to work with the original object.. This prevents unnecessary copying of the object and ensures that we work with the original object. The copy constructor initializes the month and day values of the new object by directly copying them from the existing object (obj). Here’s how the copy constructor is called:

C++
int main()
{
DayOfYear someDay; // Calls the default constructor
DayOfYear birthday(11, 23); // Calls the parameterized constructor
DayOfYear anotherDay(birthday); // Calls the copy constructor
return 0;
}

In this code, anotherDay is created as a copy of birthday. This is an example of shallow copy. It simply copies the values of month and day from birthday to anotherDay.

However, shallow copy can be problematic if the class uses dynamic memory (e.g., pointers). Both objects would then share the same memory, which can cause issues like double deletion.

To solve this, we use a deep copy, where each object gets its own memory for any dynamically allocated data. Here’s an example of how you can implement a deep copy:

C++
#include <iostream>
using namespace std;
class DayOfYear {
public:
int* month;
int* day;
DayOfYear()
{
month = new int(1);
day = new int(1);
}
DayOfYear(int m, int d) {
month = new int(m);
day = new int(d);
}
DayOfYear(const DayOfYear &obj) {
month = new int(*obj.month); // Deep copy of month
day = new int(*obj.day); // Deep copy of day
}
~DayOfYear() {
delete month;
delete day;
}
};
int main()
{
DayOfYear someDay; // Calls the default constructor
DayOfYear birthday(11, 23); // Calls the parameterized constructor
DayOfYear anotherDay(birthday); // Calls the copy constructor
cout << "someDay - Month: " << *someDay.month << ", Day: " << *someDay.day << endl;
cout << "birthday - Month: " << *birthday.month << ", Day: " << *birthday.day << endl;
cout << "anotherDay - Month: " << *anotherDay.month << ", Day: " << *anotherDay.day << endl;
return 0;
}

Now, with deep copy, when anotherDay is created, it gets its own separate memory for month and day. This prevents the issues that can arise with shallow copy, such as shared memory between objects. This avoids the risks associated with shallow copy, like double deletion or memory corruption.

Example

Let’s look at DayOfYear class we defined in a previous lesson:

C++
#include <iostream>
using namespace std;
class DayOfYear
{
public:
DayOfYear(int new_month, int new_day); // Declaring constructor
DayOfYear(); // Default constructor without any parameters
int myVar;
void output( );
int get_month( );
int get_day( );
private:
void check_date( );
int month;
int day;
};
int main(){
DayOfYear birthday(11,23); // Creating object and calling constructor
DayOfYear today; // Creating object and calling default constructor
cout << "Birthday day is: " << birthday.get_day()<<endl;
cout << "Birthday month is: "<< birthday.get_month()<<endl;
cout << "Today the day is: " << today.get_day()<<endl;
cout << "Today month is: " << today.get_month()<<endl;
return 0;
}
// Defining constructor
DayOfYear::DayOfYear(int new_month, int new_day){ // Class name and constructor name are same
month = new_month;
day = new_day;
}
DayOfYear::DayOfYear(){ // Defining default constructor
month = 0;
day = 0;
}
int DayOfYear::get_month( )
{
return month; // Returns the private variable month
}
int DayOfYear::get_day( )
{
return day; // Returns the private variable day
}
  • Line 22: The parameterized constructor is called. Here are some key points to note regarding the constructor:

    • It is called in object declaration.

    • It creates a DayOfYear object.

    • Then calls the constructor to initialize variables.

  • Line 23: The default constructor is automatically called when we create an object. This constructor takes no parameters and just initializes month and day to 0.

Constructor overloading

Just like functions, constructors can be overloaded by defining constructors with different parameter lists. For example, other possible constructors for DayOfYear class can be the following:

C++
DayOfYear(); // Default
DayOfYear(int newmonth, int newday); // Passing two int parameters
DayOfYear(double newmonth, double newday); // Passing two double parameters
DayOfYear(float newday); // Passing a float parameter

Destructor

Like a constructor, a destructor is a special member function that is automatically called when an object goes out of scope or is explicitly deleted. We use the destructors to clean up resources that the object may have acquired, such as memory. Unlike constructors, destructors cannot take arguments or return values, and each class can have only one destructor.

Syntax

Like constructors, a destructor is defined with the same name as the class, preceded by a tilde (~). It does not have any parameters or a return type.

C++
class DayOfYear {
public:
// Constructor declarations
DayOfYear(int new_month, int new_day);
DayOfYear();
// Destructor declaration
~DayOfYear();
};
// Destructor definition
DayOfYear::~DayOfYear() {
// Cleanup code (if necessary)
}

Quiz

To test your comprehension of the concepts discussed in this lesson, answer the questions given below:

Technical Quiz
1.

What is the purpose of a constructor in C++?

A.

To allocate memory for an object

B.

To define the data members of a class

C.

To initialize the object when it is created

D.

To destroy an object when it goes out of scope


1 / 3