94

Internally and about the generated code, is there a really difference between :

MyClass::MyClass(): _capacity(15), _data(NULL), _len(0)
{
}

and

MyClass::MyClass()
{
  _capacity=15;
  _data=NULL;
  _len=0
}

thanks...

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
Stef
  • 3,691
  • 6
  • 43
  • 58

12 Answers12

79

You need to use initialization list to initialize constant members,references and base class

When you need to initialize constant member, references and pass parameters to base class constructors, as mentioned in comments, you need to use initialization list.

struct aa
{
    int i;
    const int ci;       // constant member

    aa() : i(0) {} // will fail, constant member not initialized
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0) { ci = 3;} // will fail, ci is constant
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0), ci(3) {} // works
};

Example (non exhaustive) class/struct contains reference:

struct bb {};

struct aa
{
    bb& rb;
    aa(bb& b ) : rb(b) {}
};

// usage:

bb b;
aa a(b);

And example of initializing base class that requires a parameter (e.g. no default constructor):

struct bb {};

struct dd
{
    char c;
    dd(char x) : c(x) {}
};

struct aa : dd
{
    bb& rb;
    aa(bb& b ) : dd('a'), rb(b) {}
};
stefanB
  • 77,323
  • 27
  • 116
  • 141
  • 5
    And if `_capacity`, `_data` and `_len` have class types without an accessible default constructors? – CB Bailey Jan 03 '11 at 23:18
  • 2
    You call what constructors are available, if you need to set more values then you call them from body of your constructor. The difference here is that you can't initialize `const` member in body of constructor, you have to use initialization list - non `const` members can be initialized in initialization list or in the body of constructor. – stefanB Jan 03 '11 at 23:33
  • @stefan: You can't call constructors. A class with no default constructor has to be initialized in the initializer list, just like const members. – GManNickG Jan 03 '11 at 23:36
  • 2
    you also must initialize references in initialization list – Andriy Tylychko Jan 03 '11 at 23:42
  • @stefanB: I was just prompting you to correct the generalisation in your bold sentence but your comment shows a misunderstanding. You can only initialize bases and members in the initializer list, once you enter the body of the constructor they have been initialized. You may be able to assign them new values but the chance to initialize them has passed. – CB Bailey Jan 03 '11 at 23:48
  • If there is no constructor then you can't call it - that's true, but the question is about the difference. The difference is that you need to use `initialization list` to initialize `const` members. If you omit initialization of `const` member it will not compile. This assumes that there is some constructor available and you call it with required parameters (or non if that is the case). Similarly you could argue that you can't call copy constructor or assignment operator in **the body of constructor** if both are declared private - which is beside the point here. – stefanB Jan 03 '11 at 23:50
  • @stefanB: I think you're missing my point. You can't "call" any constructor for any base or member - `const` or not - from the body of the constructor. If you want to choose a non-default constructor for a base or member, you _have_ to use the initializer list. Whatever you do to the bases and members in the body of the constructor can't be initialization, only assignment or other modification. – CB Bailey Jan 04 '11 at 00:02
  • @charles I wasn't trying to imply to call constructors from the body of constructor I'm trying to explain the usage of `initialization list`, I've clearly stated that I'm talking about usage of `initialization list` ... sorry if it's not clear, I've added examples. – stefanB Jan 04 '11 at 00:06
  • 2
    @stefanB: My apologies if I've implied you don't understand this when you actually do. In your answer you have only stated when a base or member must be named in the initializer-list, you haven't explained what the conceptual difference between initializing in the initializer-list and assigning in the constructor body actually is which is where my misunderstanding may have come from. – CB Bailey Jan 04 '11 at 00:14
63

Assuming that those values are primitive types, then no, there's no difference. Initialization lists only make a difference when you have objects as members, since instead of using default initialization followed by assignment, the initialization list lets you initialize the object to its final value. This can actually be noticeably faster.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • 19
    like Richard said, it makes a difference if those values are primitive types and const, initialization lists are the only way to assign values to const members. – thbusch Jan 03 '11 at 23:14
  • 12
    It only works as described when the variables are not references or constant, if they are either constant or reference it will not even compile without using initialization list. – stefanB Jan 04 '11 at 23:00
19

Yes. In the first case you can declare _capacity, _data and _len as constants:

class MyClass
{
private:
    const int _capacity;
    const void *_data;
    const int _len;
// ...
};

This would be important if you want to ensure const-ness of these instance variables while computing their values at runtime, for example:

MyClass::MyClass() :
    _capacity(someMethod()),
    _data(someOtherMethod()),
    _len(yetAnotherMethod())
{
}

const instances must be initialized in the initializer list or the underlying types must provide public parameterless constructors (which primitive types do).

Richard Cook
  • 32,523
  • 5
  • 46
  • 71
  • 2
    The same goes for references. If your class has reference members, they *must* be initialized in the initializer list. – Mark Ransom Jan 03 '11 at 23:40
7

I think this link http://www.cplusplus.com/forum/articles/17820/ gives an excellent explanation - especially for those new to C++.

The reason why intialiser lists are more efficient is that within the constructor body, only assignments take place, not initialisation. So if you are dealing with a non-built-in type, the default constructor for that object has already been called before the body of the constructor has been entered. Inside the constructor body, you are assigning a value to that object.

In effect, this is a call to the default constructor followed by a call to the copy-assignment operator. The initialiser list allows you to call the copy constructor directly, and this can sometimes be significantly faster (recall that the initialiser list is before the body of the constructor)

user929404
  • 2,153
  • 1
  • 22
  • 27
5

I'll add that if you have members of class type with no default constructor available, initialization is the only way to construct your class.

Fred Larson
  • 60,987
  • 18
  • 112
  • 174
3

A big difference is that the assignment can initialize members of a parent class; the initializer only works on members declared at the current class scope.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
2

Depends on the types involved. The difference is similar between

std::string a;
a = "hai";

and

std::string a("hai");

where the second form is initialization list- that is, it makes a difference if the type requires constructor arguments or is more efficient with constructor arguments.

Puppy
  • 144,682
  • 38
  • 256
  • 465
1

The real difference boils down to how the gcc compiler generate machine code and lay down the memory. Explain:

  • (phase1) Before the init body (including the init list): the compiler allocate required memory for the class. The class is alive already!
  • (phase2) In the init body: since the memory is allocated, every assignment now indicates an operation on the already exiting/'initialized' variable.

There are certainly other ways to handle const type members. But to ease their life, the gcc compiler writers decide to set up some rules

  1. const type members must be initialized before the init body.
  2. After phase1, any write operation only valid for non-constant members.
lukmac
  • 4,617
  • 8
  • 33
  • 34
1

There is only one way to initialize base class instances and non-static member variables and that is using the initializer list.

If you don't specify a base or non-static member variable in your constructor's initializer list then that member or base will either be default-initialized (if the member/base is a non-POD class type or array of non-POD class types) or left uninitialized otherwise.

Once the constructor body is entered, all bases or members will have been initialized or left uninitialized (i.e. they will have an indeterminate value). There is no opportunity in the constructor body to influence how they should be initialized.

You may be able to assign new values to members in the constructor body but it is not possible to assign to const members or members of class type which have been made non-assignable and it is not possible to rebind references.

For built in types and some user-defined types, assigning in the constructor body may have exactly the same effect as initializing with the same value in the initializer list.

If you fail to name a member or base in an initializer list and that entity is a reference, has class type with no accessible user-declared default constructor, is const qualified and has POD type or is a POD class type or array of POD class type containing a const qualified member (directly or indirectly) then the program is ill-formed.

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
0

There is a difference between initialization list and initialization statement in a constructor. Let's consider below code:

#include <initializer_list>
#include <iostream>
#include <algorithm>
#include <numeric>

class MyBase {
public:
    MyBase() {
        std::cout << __FUNCTION__ << std::endl;
    }
};

class MyClass : public MyBase {
public:
    MyClass::MyClass() : _capacity( 15 ), _data( NULL ), _len( 0 ) {
        std::cout << __FUNCTION__ << std::endl;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

class MyClass2 : public MyBase {
public:
    MyClass2::MyClass2() {
        std::cout << __FUNCTION__ << std::endl;
        _capacity = 15;
        _data = NULL;
        _len = 0;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

int main() {
    MyClass c;
    MyClass2 d;

    return 0;
}

When MyClass is used, all the members will be initialized before the first statement in a constructor executed.

But, when MyClass2 is used, all the members are not initialized when the first statement in a constructor executed.

In later case, there may be regression problem when someone added some code in a constructor before a certain member is initialized.

0

Here is a point that I did not see others refer to it:

class temp{
public:
   temp(int var);
};

The temp class does not have a default ctor. When we use it in another class as follow:

class mainClass{
public:
 mainClass(){}
private:
  int a;
  temp obj;
};

the code will not compile, cause the compiler does not know how to initialize obj, cause it has just an explicit ctor which receives an int value, so we have to change the ctor as follow:

mainClass(int sth):obj(sth){}

So, it is not just about const and references!

hosh0425
  • 98
  • 1
  • 10
0

If you write an initializer list, you do all in one step; if you don't write an initilizer list, you'll do 2 steps: one for declaration and one for asign the value.

ssube
  • 47,010
  • 7
  • 103
  • 140