C++

C++ : Member Initializer List

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

  • Employee as the container class
    • class String name ( contained class )
    • class Date date_of_birth ( contained class )
Constructor
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.
}

We notice the below sequence of constructors getting called when an object of the class Employee is created on the stack.
  • The default constructors of the contained objects ( String and Date ) get called.
  • The parameterized constructor of the class Employee gets called with the statement [ Employee ( const int arg_id, const char * arg_name, int arg_dd, int arg_mm, int arg_yy ) ]
  • The parameterized constructor of the class String gets called with the statement [ m_name = String ( arg_name ) ] to create a temporary object.
  • For assigning the temporary object to m_name the overloaded assignment operator of the class String gets called.
  • The parameterized constructor of the class Date gets called with the statement [ m_dob = Date ( arg_dd, arg_mm, arg_yy ) ]

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,

  • Parameterized constructor gets called for class String.
  • Parameterized constructor gets called for class Date.
  • Parameterized constructor gets called for class Employee.

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.



Copyright (c) 2019-2021, Algotree.org.
All rights reserved.