7

On some tutorials (e.g. http://www.tutorialspoint.com/cplusplus/cpp_constructor_destructor.htm) I read that the following two codes are equivalent.

First code:

class MyClass1{
    public:
        int a;
        int b;
        MyClass1(int a, int b) : a(a), b(b) {};
};

Second code:

class MyClass2{
    public:
        int a;
        int b;
        MyClass2(int, int);
};

MyClass2::MyClass2(int a, int b){
    this->a = a;
    this->b = b;
}

In fact, they give me the same results. But, if I use const members I'm not able to compile the code anymore.

class MyClass1{
    public:
        const int a;
        const int b;
        MyClass1(int a, int b) : a(a), b(b) {};
};

class MyClass2{
    public:
        const int a;
        const int b;
        MyClass2(int, int);
};

MyClass2::MyClass2(int a, int b){
    this->a = a;
    this->b = b;
}

In fact the first class give me no error but in the second class there is an assignment of read-only member. So, these are the questions:

What is the real difference among the two methods of initialization?

Is using the initialization lists the only method to initialize const members of a class?

Note: I read online the use of delegating constructors to avoid this problem but it's not clear for me their use and what they really do.

gvgramazio
  • 1,115
  • 3
  • 13
  • 30
  • 1
    The tutorial is wrong in saying that those forms are equivalent. I'd recommend getting a good [introductory book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) rather than relying on inaccurate online tutorials. – TartanLlama Nov 04 '15 at 11:35
  • 3
    The difference is that one of them isn't an initialization. – juanchopanza Nov 04 '15 at 11:35

3 Answers3

7

A simple way of looking at it is to make connections with local variables:

  1. Using initializer lists is equivalent to this view of local variables:

    int a = 1;
    int b = 2;
    
  2. The second form, assigning them inside constructor is equivalent to this:

    int a;
    int b;
    
    a = 1;
    b = 2;
    

You can see how this can be a problem with const or with objects that don't have a default constructor:

Const members

  1. Ok:

    const int a = 1;
    const int b = 2;
    
  2. Not ok:

    const int a;
    const int b;
    a = 1;
    b = 2;
    

Types with deleted or not accessible default constructor

e.g.:

class X {
public:
   X() = delete; // default constructor deleted
   X(int){};     // constructor with an int parameter
};
  1. Ok:

    X x(1);
    
  2. Not ok:

    X x;
    x = X(1);
    

3rd option: In-class member initializers (since c++11)

class A {
public:
   const int a = 10;
};
bolov
  • 72,283
  • 15
  • 145
  • 224
  • You are very clear. So is through initialization lists the only way to initialize `const` members? – gvgramazio Nov 04 '15 at 11:42
  • @giusva Or since c++ 11 or 14 (can't remember now) through a `constexpr` you can provide a "default" value in definition. – luk32 Nov 04 '15 at 11:47
  • @bolov: the 3rd option is not a valid one. It's trivial that I can initialize it during declaration of the class using a const value. I want a way to pass the value (or, if it is the case, to compute it) when i declare the object of that class calling its constructor. – gvgramazio Nov 04 '15 at 11:51
  • @giusva It's valid. It is just not helpful to your situation. I'll leave it in the answer for completeness. – bolov Nov 04 '15 at 11:53
  • @giusva No, it is not trivial, it wasn't allowed until c++ 11 with exception for `int`. And you asked if it's the only way. The answer is, no, it isn't, even if the other option would be trivial. – luk32 Nov 04 '15 at 11:55
  • so if you want to assign with a parameter value, then yes, initializer list is your only option. This is however not a bad thing since you should use initializer lists anyway and avoid the second form all together – bolov Nov 04 '15 at 11:55
  • Sorry for the improper use of "valid". I mean that it's not helpful to the situation since the trouble is to store a desired value in the constant member of an object chosen during the initialization of the object itself. In the way you suggest, I have to chose the value when I wrote the code. Anyway, I didn't suggest to cancel it from your question (it could be helpful to someone in the future). – gvgramazio Nov 04 '15 at 12:10
4

You don't actually ever initialise any member varaibles in the constructor body, that's the chief difference.

All base members are initialised before the constructor body is entered. The initialiser list helps you do that. If any members are missing from the initialiser list then they are default constructed, aside from POD types which are not initialised. If a member does not have an accessible default constructor, then you must include it in the initialiser list with appropriate arguments.

Note that the order of initialisation of members in a base member initialisation list is not the order that they appear in the initialisation list itself, but the order they appear in the class declaration: If you had written MyClass1(int a, int b) : b(b), a(a) {}; then a would still be initialised first. It's very important that you remember that.

Using base member initialisation whenever possible is preferred as it tends to lead to greater program stability.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • Thanks for the tip about the order of initialization, that can be very useful. Anyway, why the use of initialization list lead to greater stability? – gvgramazio Nov 04 '15 at 11:36
  • 1
    @giusva If you use initialization lists, this leaves fewer places in code where the variables could be uninitialized. Also, they make it possible to use `const` and noncopyable members, which make your program's behavior more constrained. – anatolyg Nov 04 '15 at 11:50
  • @anatolyg: So, if I initialize the const members with initialization lists there is no problem. But, if i didn't want to pass the value of that member directly to the constructor because I need to compute it inside the constructor, how can i do it? e.g. I want to pass two values when i declare an object of that class and i need to store the sum of them in a constant member of that object (I know that I can do the sum inside the initialization list but suppose to have a more complex operation to do) – gvgramazio Nov 04 '15 at 11:59
  • @giusva You may want to ask a separate question for that. – anatolyg Nov 04 '15 at 12:22
  • 1
    @giusva Write a function that returns the value and call it from the initialization list. – molbdnilo Nov 04 '15 at 12:26
2

In the first case when a data member is initialized in the mem-initializer list there is called its constructor, that is the data member is created.

In the second case at first data members are default initialized and then there is used the corresponding copy assignment operator. That is in the second case there are to steps of the process:

  1. creating an object with the default constructor;
  2. calling the copy assignment operator to assign a value to the object.

For example constant objects or references shall be initialized when they are created. So you may not at first create them without an initializer using the default constructor and then reassign them. Or for example an object can have no default costructor. In this case you have to initialize it explicitly in the mem-initializer list using a constructor with parameters.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • I don't understand one thing: when I declare a constructor, do it overwrite the default constructor? Can you do some clarifier example? – gvgramazio Nov 04 '15 at 11:39
  • 1
    @giusva WHen you explicitly declare a constructor then the default constructor is not declared and defined. If you want to have also a default constructor then in this case it also should be declared explicitly. – Vlad from Moscow Nov 04 '15 at 11:41