Initializer Lists play an important role in C++, and are used in a variety C++ Containers such as Arrays and Vectors. One such popular use of a C++ Initializer List, is with a Class Constructor, when initializing member variables or calling parent constructors.
Initializing constant member variables
One of the most common uses of the Initializer List in Constructors is to initialize constant data variables. Usually, when declaring constant variables, we need to assign them a value at the same time they are created. Like the example below.
const int data = 5;
However, we can’t do the same when it comes to Classes.
If you try to run the below code, you will be met with an error, that says that the const variable data
is read-only.
#include <iostream>
using namespace std;
class Class {
public:
const int data;
Class(int d) {
data = d;
}
};
int main() {
Class object = Class(5);
}
The reason behind this problem, is that a const variable cannot be assigned a value after the Constructor has been executed, and memory has been allocated. Now, how do we solve this problem? This is where the C++ Constructor Initializer list comes in.
The Constructor Initializer List runs before the Constructor, and before any memory is assigned. Hence this allows us to initialize Constant Data inside of it. An example of how this is done is shown below.
#include <iostream>
using namespace std;
class Class {
public:
const int data;
Class(int d) : data(d) { }
};
int main() {
Class object = Class(5);
}
To try and explain the above code, everything between the semi-colon and the starting curly bracket of the Constructor, is the Constructor Initializer list. What we are doing is basically passing the parameter d
into the Constructor of the variable data
. (Remember that data
/integers are objects too)
This technique is not limited to just constant data members, but often used to initialize all members. Continue reading to find out the other various uses and reasons we use the Constructor Initializer List.
Initializing reference member variables
Similar to Constant variables, we need to use the Constructor Initializer List to initialize reference variables as well. We could achieve this without the Initializer List, but it would be less efficient and a bit of a workaround rather than a proper solution.
#include <iostream>
using namespace std;
class Class {
public:
string &refToString;
Class(string &r) : refToString(r) {
refToString = "Goodbye World";
}
};
int main() {
string str = "Hello World";
Class object2 = Class(str);
cout << str << endl;
}
Goodbye World
As you can see from the output, the reference was stored successfully, and changes to it were reflected in the original.
Calling Parameterized Constructors
As you might have already noticed, the Constructor Initializer List can be used to call parameterized Constructor. By default, all member variables are defined in the following manner, regardless of what their type is.
class ClassB { };
class ClassA {
int data;
string str;
ClassB objB;
}
What’s happening here, in the example above is that the default Constructor is being called for all three objects.
Now what if Class B didn’t have a default constructor? Or maybe we want to call a specific parameterized Constructor? That’s where we use the Initializer List. Within the Initializer list, we can call whatever constructor we want, which is what we have been doing uptil now in this tutorial.
The below example uses the Initializer List to call the new Constructor for ClassB that we defined.
#include <iostream>
using namespace std;
class ClassB {
public:
ClassB (int n, string s) {
cout << "Parameterized Constructor Called" << endl;
}
};
class ClassA {
public:
int data;
string str;
ClassB objB;
ClassA(int n, string s) : objB(n, s) {}
};
int main() {
ClassA object = ClassA(1, "Bob");
}
Optimization and Performance
Another important reason behind this, is performance and optimization. If you think about this in terms of performance, memory and how much code needs to be executed, you will realize the Initializer List is significantly more efficient.
Let’s analyze what happens when you do not use an Initializer list.
class Class {
public:
int data;
string str;
Class(int n, string s) {
data = n;
str = s;
}
};
- First, you pass into the Class Constructor, a bunch of parameters. This calls the copy constructor on them all. Because the values are being copied over.
- Next, before anything inside the constructor executes, all the member variables get “default constructed” which means the default constructors for them are called.
- Next the parameter values are assigned to the member variables using the assignment constructor. Remember, the copy constructor will not be used here, as the member variables have already been initialized.
So we can see that three operations were required. First the copy constructor, then the default constructors, then the assignment constructor. Now, let’s see what happens with Initializer Lists.
class Class {
public:
int data;
string str;
Class(int n, string s) : data(n), str(s) { }
};
- First, you pass into the Class Constructor, a bunch of parameters. This calls the copy constructor on them all.
- Before the Constructor actually executes, and the member variables are allocated memory, we call their parameterized Constructors (like we showed earlier). They now have been initialized with the values we want them to have.
In short, without the initializer Lists, three steps are needed. Three Constructors are called. With the Initializer Lists however, one step is removed, improving performance.
This marks the end of the C++ Constructor Initializer List Tutorial. Any suggestions or contributions for CodersLegacy are more than welcome. Questions regarding the tutorial content can be asked in the comments section below.