1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

C++ Initialization List

Discussion in 'C++' started by BiplabKamal, Mar 31, 2016.

  1. BiplabKamal

    BiplabKamal New Member

    Joined:
    Dec 30, 2015
    Messages:
    36
    Likes Received:
    5
    Trophy Points:
    0
    Occupation:
    Software Engineer
    Location:
    Kolkata
    The words Initialization List indicates that it is related to initializing something. What is to be initialized? Ok, when memory is allocated for any object of any type (primitive or user defined) the content of the memory can be undefined or set to some standard value (preferably zero) specific to memory allocation scheme. This may depend on the compiler or OS functions used to allocate memory. Some compiler may just allocate the memory and leave it unchanged in which case the value just after allocation can be garbage. Some compiler may set the bits to zero just after allocating the memory. In either case it is good practice that the programmer specify the initial values that suits his purpose while creating the objects into the memory. For example you can use either of the following two lines to define a variable of int type.
    Code:
    int i; // uninitialized
        or
    int i = 0; // initialized
    
    Which line of code below do you think is better? I hope no one will object that int i=0; is safer because we don’t know in case of the 1st line what will be the value just after allocating the memory. You may argue that you can use following two lines instead.
    Code:
    int i; 
    i = 10; // This is not initialization but assignment
    
    Off-course you can but it may do one step extra but writing int i = 0; does not give any performance penalty. Why? Suppose the 1st line ( int i; ) results in two actions i. e. allocate the memory and set the bits to zero. So above two lines code will result in three actions: allocate, set to zero, set to 10; But if we write int i = 10; it will result only two actions: allocate and set to 10. This is called initialization. Initialization is setting the memory content of an object to an initial value before the object is in ready to use state. It is done along with the memory allocation before doing any operation on that object.

    So far we discussed about initialization of a primitive type. But what about user defined types like a class? OK, Initialization code for class instance is written in the class constructor and copy constructor. Compiler generate the code such that the constructor or the copy constructor is called just after memory allocation. If we have a class named ‘MyClass’ the following lines will allocate memory and call it’s constructor/copy constructor to initialize the memory content.
    Code:
    MyClass obj; // calls constructor
    MyClass obj2 = obj; // calls copy constructor
    
    It’s the constructor or copy constructor’s responsibility how it assign the values to it’s members and base objects. Either it can use initialization list or use assignment operators inside the constructor. Although we discussed that initialization is more efficient. You know the member object’s constructors are called before container object’s constructor code is executed. Similarly the constructor of base class is called before executing the derived constructor. This is because the object’s constructor can use the members or the base object, so they should be ready before the objects constructor start execution. This is known as member initialization. Let us take the following example where MyClass has a data member of int type.
    Code:
    class MyClass
    {
    private:
        int data =0; // initialization
    public:
        MyClass(int value):data(value) // Member initialization through constructor
        {
            data = value; // This is not initialization though out come is same
        }
    };
    
    Note that the non-static data member can be set to a desired initial value in three ways shown. The code to set the value inside the constructor is not initialization rather assignment. The other two are initializations. Initialization through constructor is done via initialization list added in the constructor deceleration after putting a colon and separated by coma. Initialization in the constructor is preferred because the user of the class can pass the default value. Advantage of initialization over assignment will be more clear in the following example:
    Code:
    #include<iostream>
    using namespace std;
    class MyClass
    {
    private:
        static int instancecount;
        int data;
    public:
    
        MyClass():data(0)
        {
            cout << "New instance of MyClass is created" << endl;
            cout << "Number of instances: " << ++instancecount << endl;
        }
        MyClass(const MyClass& source):data(source.data)
        {
            cout << "New instance of MyClass is created by copy constructor" << endl;
            cout << "Number of instances: " << ++instancecount << endl;
        }
        MyClass& operator =(MyClass& source)
        {
            data = source.data;
            cout << "Copy assignment operator is called" << endl;
            return *this;
        }
        ~MyClass()
        {
            cout << "One instance of MyClass is destroyed" << endl;
            cout << "Number of instances: " << --instancecount << endl;
        }
    
    };
    int MyClass::instancecount = 0; 
    
    int main()
    {    
        ////Initialization by constructor
        MyClass obj; 
    
        //Initialization by copy constructor
        MyClass obj2(obj); 
    
        // Initialization by constructor and assignment operator
        MyClass obj3;
        obj3 = obj;
        return 0;
    }
    
    Now we will see how it works if class members are instances of classes. If we create a wrapper class of MyClass like:
    Code:
    class MyClassWrapper
    {
    private:
        MyClass myobj;
    public:
        MyClassWrapper(MyClass obj) :myobj(obj)
        {
        }
    };
    
    and main function like:
    Code:
    int main()
    {
        MyClass myobj;
        MyClassWrapper wrapper(myobj);
        return 0;
    }
    
    The member initialization list will result in a copy constructor call of MYClass but if we define the wrapper like:
    Code:
    class MyClassWrapper
    {
    private:
        MyClass myobj;
    public:
        MyClassWrapper(MyClass obj)
        {
            myobj = obj;
        }
    };
    
    Then the constructor and copy assignment operator will get called. This shows that initializing in the constructor’s initialization list is more efficient than assigning the default value within the constructor.

    Even we don’t bother about efficiency of the code there are cases where you must use constructor initialization list. These are the cases where you are bound to initialize a member object. For example constant type variables and reference type variables. You have to initialize members of constant type and reference type and you know the initialization can be done through initialization list only. Also if the member object or base class does not have a default constructor or you don’t want to call the default constructor, you have to again use initialization list. Following example will show these cases:

    Code:
    
    #include<iostream>
    using namespace std;
    class MyClass
    {
    private:
        static int instancecount;
        int data;
    
    public:
    
        MyClass(int value):data(value) // Initialization is not must
        {
            cout << "New instance of MyClass is created" << endl;
            cout << "Number of instances: " << ++instancecount << endl;
        }
        MyClass(const MyClass& source):data(source.data) // Initialization is not must
        {
            //data = source.data;
            cout << "New instance of MyClass is created by copy constructor" << endl;
            cout << "Number of instances: " << ++instancecount << endl;
        }
        MyClass& operator =(MyClass& source)
        {
            data = source.data;
            cout << "Copy assignment operator is called" << endl;
            return *this;
        }
        ~MyClass()
        {
            cout << "One instance of MyClass is destroyed" << endl;
            cout << "Number of instances: " << --instancecount << endl;
        }
    
    };
    int MyClass::instancecount = 0;
    
    class MyClassWrapper:public MyClass
    {
    private:
        const int wrapper;
        MyClass myobj;
        MyClass& refobj;
    public:
        MyClassWrapper(int val1,int val2,MyClass& robj) :myobj(val1),wrapper(val2),refobj(robj),MyClass(val1) // Initialization is must
        {
    
        }
        MyClassWrapper(MyClassWrapper& source) :myobj(source), wrapper(source.wrapper), refobj(source),MyClass(source) // Initialization is must
        {
            
        }
    };
    int main()
    {
        MyClass myobj(100);
        MyClassWrapper wrapper(10,20,myobj);
        return 0;
    }
    
    Now there is an interesting question that if all members can be initialized with initialization list then the body of the constructor or copy constructor can be empty. If no code in the constructor then why do we need the explicit constructor? Reason is simple, you need a constructor when default constructor will not work. For example you want to pass arguments to constructor. You will to write code in constructor when you need special code/algorithm to allocate resource which cannot be written in initialization list.

    Things to remember
    • If base class does not have any default constructor, initialization list is needed
    • If the class of member object does not have default constructor initialization list is needed
    • If member is constant or reference type, initialization list is needed
    • Initializing via initialization list is more efficient than assigning inside the constructor.
     

Share This Page