tags:

views:

285

answers:

8
Sphere() : theRadius(1.0)
{
}

Why is it preferable to have a constructor written with initializers (above) than a constructor that initializes the data members within its body (below)?

Sphere()
{
     theRadius = 1.0;
}
+7  A: 

If you don't initialize fields explicitly, the compiler will try to run the default constructor of them. If they don't have an accessible default constructor, your source code will fail to compile:

class Field {
public:
    Field(int x) {}
};
class Test {
    Field f;
public: 
    Test() {  // compiler error here. `Field` doesn't have default constructor.
       f = Field(10); 
    } 
};
int main ( ) {
    Test t;
}

If they do have a default constructor, you're calling it unnecessarily as you're calling another constructor of the field in the body.

Mehrdad Afshari
+2  A: 

Because otherwise the objects default constructor is called and then the value is set.

This would call only the string constructor

class Foo{
     std::string s;
     Foo() : s("Hello World"){}
}

Where as this:

class Foo{
     std::string s;
     Foo(){
         s = "Hello World";
     }
}

Would call the default string constructor and then set the value of the string to "Hello World"

Yacoby
+15  A: 

All members are initialised before entering the body of the constructor. If you don't provide an initialiser in the init list, then they're default constructed.

In your first example the following happens:

  • theRadius is initialised to 1.0

In your second example the following happens:

  • theRadius is initialised
  • 1.0 is copied to theRadius

With simple types like ints and floats this won't matter much. however if your members are objects with non-trivial constructors / copy constructors then it's a lot more efficient to use the initialisation list approach.

Glen
Answer is good, but I would also mention that there is also disadvantage of initializers in current C++ (I think they fixed it in C++0x where you can call other constructors). You may end up with duplicated initializer lists if you happen to have class with several constructors. Calling some common init function fixes this duplication but you lose efficiency.Another problem worth mentioning is (lack of) order of initialization - you must keep an eye on it when constructing objects with dependecies like initializing with reference to "parent" object.
MaR
@MaR, I can live with the duplication if I have multiple constructors. Also there is no lack of order of initalisation. The order of initialisation is EXTREMELY well defined. Variables are always initialised in the order that they are declared in the class definition. This is mandated by the C++ standard. I can't remember the section number, but it's been quoted many times in asnwers in SO
Glen
@Glen - that's why "lack of" is in parenthesis :o) There IS an order, but you must keep an eye on it(order is in class definition(different place!) and it's not syntax error), whereas non-initializer code is really explicit.
MaR
@MaR, I see what you're saying now. however this quote "Another problem worth mentioning is (lack of) order of initialization" reads as if you're saying there is no order of initialization. Putting it in parenthesis doesn't mean what you meant it to mean
Glen
+3  A: 

A constructor is always called for object attributes before entering the constructor's body. If you don't specify a constructor yourself, the default one will be called, and operator= will be used later to change the value.

This may not be what you want. In particular, you can't change the value of a const attribute inside the constructor body. You have to use the init list.

Bastien Léonard
+1  A: 

As already said, for non-primitive types it makes a difference.

It's also the only way to initialise const member variables.

class MyClass
{
 const int x;
public: 
 MyClass(int x)
 {
  this->x=x;
 }
};

This should not compile.

John
+1  A: 

Like others have said, it's more efficient to use the first form for types with non-trivial construction.

In addition, the first form gives you the ability to catch any exceptions thrown in the constructor of the member object (or base class) that is being initialized by using the function form of a try/catch block. e.g:

MyObject() : memberObject(...)
try {
  // my constructor
}
catch (...) {
  // catches exceptions in try block AND memberObject constructor
}

That's not missing a set of brackets. If you do put another set of brackets around the try/catch block it will not catch exceptions in the memberObject constructor, just the try block.

Ferruccio
+1  A: 

It is mainly used to initialize constants and reference variable since they can't be initialized in body of constructor.

Ashish
+1  A: 

This question/answer from C++ FAQ Lite may provide more explanation.

MeThinks