C++ provides an efficient mechanism for initializing the data members of a container class using the member initializer list.
Notice the below example which shows various constructors and assignment operators getting invoked when the member initializer list is not used / used.
We have
Constructor ( without a member initializer list ) |
---|
Employee ( const int arg_id, const char * arg_name, int arg_dd, int arg_mm, int arg_yy ) { m_id = arg_id; m_name = String ( arg_name ); // Assignment operator gets invoked. m_dob = Date ( arg_dd, arg_mm, arg_yy ); // Implicit assignment operator gets invoked. } |
All these calls to the constructor(s) for initializing the object of class Employee prove quite inefficient.
Constructor with member initializer list |
---|
Employee ( const int arg_id, const char * arg_name, int arg_dd, int arg_mm, int arg_yy ) : m_id ( arg_id ), m_name ( arg_name ), m_dob (arg_dd, arg_mm, arg_yy ) { } |
With member initializer list, the number of calls to the constructor(s) is significantly reduced as only the parameterized constructor(s) are invoked and executed. Thus we have,
Note : If a class has a constant data member, the only way to initialize it, is to use a member initializer list.
C++ program demonstrates the use of member initializer list for initializing contained objects of a class.
#include<iostream>
#include<cstring>
using namespace std;
class Date {
private:
int dd, mm, yy;
public:
// Default constructor
Date () {
cout << "Default constructor got called for class Date" << endl;
}
// Parameterized constructor
Date (int arg_day, int arg_mon, int arg_year) {
cout << "Parameterized constructor got called for class Date" << endl;
dd = arg_day;
mm = arg_mon;
yy = arg_year;
}
// Destructor
~Date () {
cout << "Destructor got called for class Date" << endl;
}
};
class String {
private:
int m_len;
char* m_buff;
public:
// Default constructor
String () {
m_len = 0;
m_buff = new char;
m_buff[0] = '\0';
cout << "Default constructor got called for class String" << endl;
}
// Parameterized constructor
String (const char* str) {
cout << "Parameterized constructor got called for class String" << endl;
m_len = strlen(str);
m_buff = new char[m_len + 1];
strcpy(m_buff, str);
}
// Overload assignment operator to avoid dangling pointer that would cause errors.
String& operator = (const String& obj) {
cout << "Assignment operator got called for class String" << endl;
if (this == &obj) {
return (*this);
} else {
m_len = obj.m_len;
delete [] m_buff; // Delete the old buffer of destination object.
m_buff = new char[m_len + 1];
strcpy(m_buff, obj.m_buff);
return (*this);
}
}
// Destructor
~ String() {
cout << "Destructor got called for class String" << endl;
if (m_buff) {
delete [] m_buff;
}
}
};
class Employee {
private:
int m_id;
String m_name;
Date m_dob;
public:
Employee (const int arg_id, const char * arg_name, int arg_dd, int arg_mm, int arg_yy) {
cout << "Parameterized constructor got called for class Employee" << endl;
m_id = arg_id;
m_name = String(arg_name); // Assignment operator gets invoked. Hence, overloaded in Employee class.
m_dob = Date(arg_dd, arg_mm, arg_yy); // Implicity assignment operator gets invoked but overloading is not needed.
}
~ Employee() {
cout << "Destructor got called for class Employee" << endl;
}
};
int main() {
Employee emp_1(42, "Skywalker", 20, 1, 2000);
return 0;
}
Output
Default constructor got called for class String
Default constructor got called for class Date
Parameterized constructor got called for class Employee
Parameterized constructor got called for class String
Assignment operator got called for class String
Destructor got called for class String
Parameterized constructor got called for class Date
Destructor got called for class Date
Destructor got called for class Employee
Destructor got called for class Date
Destructor got called for class String
#include<iostream>
#include<cstring>
using namespace std;
class Date {
private:
int dd, mm, yy;
public:
// Default constructor
Date () {
cout << "Default constructor got called for class Date" << endl;
}
// Parameterized constructor
Date (int arg_day, int arg_mon, int arg_year) {
cout << "Parameterized constructor got called for class Date" << endl;
dd = arg_day;
mm = arg_mon;
yy = arg_year;
}
// Destructor
~Date () {
cout << "Destructor got called for class Date" << endl;
}
};
class String {
private:
int m_len;
char* m_buff;
public:
// Default constructor
String () {
m_len = 0;
m_buff = new char;
m_buff[0] = '\0';
cout << "Default constructor got called for class String" << endl;
}
// Parameterized constructor
String (const char* str) {
cout << "Parameterized constructor got called for class String" << endl;
m_len = strlen(str);
m_buff = new char[m_len + 1];
strcpy(m_buff, str);
}
// Overload assignment operator to avoid dangling pointer that would cause errors.
String& operator = (const String& obj) {
cout << "Assignment operator got called for class String" << endl;
if (this == &obj) {
return (*this);
} else {
m_len = obj.m_len;
delete [] m_buff; // Delete the old buffer of destination object.
m_buff = new char[m_len + 1];
strcpy(m_buff, obj.m_buff);
return (*this);
}
}
// Destructor
~ String() {
cout << "Destructor got called for class String" << endl;
if (m_buff) {
delete [] m_buff;
}
}
};
class Employee {
private:
int m_id;
String m_name;
Date m_dob;
public:
/*
Employee (const int arg_id, const char * arg_name, int arg_dd, int arg_mm, int arg_yy) {
cout << "Parameterized constructor got called for class Employee" << endl;
m_id = arg_id;
m_name = String(arg_name); // Assignment operator gets invoked. Hence, overloaded in Employee class.
m_dob = Date(arg_dd, arg_mm, arg_yy); // Implicity assignment operator gets invoked but overloading is not needed.
} */
// Parameterized constructor with a member initializer list.
Employee (const int arg_id, const char * arg_name, int arg_dd, int arg_mm, int arg_yy) : \
m_id(arg_id), m_name(arg_name), m_dob(arg_dd, arg_mm, arg_yy) {
cout << "Parameterized constructor got called for class Employee" << endl;
}
~ Employee() {
cout << "Destructor got called for class Employee" << endl;
}
};
int main() {
Employee emp_1(42, "Skywalker", 20, 1, 2000);
return 0;
}
Output
Parameterized constructor got called for class String
Parameterized constructor got called for class Date
Parameterized constructor got called for class Employee
Destructor got called for class Employee
Destructor got called for class Date
Destructor got called for class String
#include<iostream>
class Employee {
private :
const int m_empid;
public:
// The only way to initialize a constant member of a class
// is to use a member initializer list
// Note : The compiler throws the below error if the constant member employee id
// is tried to initialize without using a member initializer list.
// "error: assignment of read-only member ‘Employee::m_empid’
Employee (int id) : m_empid (id) {
std :: cout << "Parameterized constructor got called" << std :: endl;
}
void Display() {
std :: cout << "Employee Id : " << m_empid << std :: endl;
}
~Employee () {
std :: cout << "Destructor got called." << std :: endl;
}
};
int main() {
Employee e(42);
e.Display();
return 0;
}
Output
Parameterized constructor got called
Employee Id : 42
Destructor got called.