HI, I have a question but cannot find the answer for a long time, that is, what's difference between the following 2 statements about the parameter initialization?
class A::A()
: a(0), b(0), c(0)
{
}
class A::A()
{
a = 0
b = 0;
c = 0;
}
I know there is "direct initialization" and "copy initialization", but I don't know what's the other differences and if there is any description about the first statement?
Thanks in advance
Using the initializer list, the members are created and initialized only once, with the given value.
Using assignment, the members are initialized with a default value, then reassigned in the constructor body.
In some cases (constants, references) you can only use initialization lists because
- these must be initialized with a valid value, and
- once they are constructed, reassigning them is illegal.
In other cases, even though assignment is possible, initialization list is still preferable as it avoids extra work (double initialization, which can be costly for some types) and adheres to a common idiom, making your code easier to understand and maintain.
One caveat which is good to know is that the order of initialization of members is defined by their order of declaration, not by the order in which they are listed in the initialization list. E.g.
class Example {
int a, b, c;
Example() : a(1), c(2), b(c) {}
}
yields undefined behaviour because b
is initialized before c
, hence with an undefined value. To avoid confusion and the potential for such subtle bugs, always list members in the initialization list in the same order as they're declared in the class.
This may seem obscure at first, but there is a reason for it. In C++, it is guaranteed that all members of a class are destroyed in exactly the reverse of the order they were created. Now, classes can have multiple constructors, each with its own initialization list, and (unfortunately, one might say) initialization lists can be ordered any way the programmer wants. If the order of the initialization list determined the actual order of initialization, the runtime should somehow maintain data about each object to remember which constructor it was created with, and in what order its members should be destroyed. This would incure runtime overhead, for no obvious benefit, so - in line with the general C++ philosophy of "pay only for what you use" - it was decided that the initialization order is defined by the declaration order once and for all.
The purpose of default initialization list is to initialize the constant variable with in the class. Because the constant variable is initialized before the object is initialized.
I provide one sample to explain the difference between these two initializations:
class A
{
private:
const int x;
};
A::A():x(5) //this code works fine
{
}
A::A() //this code is wrong.const variable is not initialized once object
{
x=5;
}
The main difference is, that in first case the clas members are initialized and in the second case they are assigned. For non integral types it means, that in the second case you will use operator=
to assign values to your class members.
Commonly, it's preferred to use first case, because in that case class memebers are initialized before the constructor body.
Also, you can't use assignment on several cases, for example, when class member is declared const.
Taken from section 10.6 of the C++ FAQ by Marshall Cline:
Initialization lists. In fact, constructors should initialize as a rule all member objects in the initialization list. One exception is discussed further down.
Consider the following constructor that initializes member object x_ using an initialization list: Fred::Fred() : x_(whatever) { }. The most common benefit of doing this is improved performance. For example, if the expression whatever is the same type as member variable x_, the result of the whatever expression is constructed directly inside x_ — the compiler does not make a separate copy of the object. Even if the types are not the same, the compiler is usually able to do a better job with initialization lists than with assignments.
The other (inefficient) way to build constructors is via assignment, such as: Fred::Fred() { x_ = whatever; }. In this case the expression whatever causes a separate, temporary object to be created, and this temporary object is passed into the x_ object's assignment operator. Then that temporary object is destructed at the ;. That's inefficient.
As if that wasn't bad enough, there's another source of inefficiency when using assignment in a constructor: the member object will get fully constructed by its default constructor, and this might, for example, allocate some default amount of memory or open some default file. All this work could be for naught if the whatever expression and/or assignment operator causes the object to close that file and/or release that memory (e.g., if the default constructor didn't allocate a large enough pool of memory or if it opened the wrong file).
Conclusion: All other things being equal, your code will run faster if you use initialization lists rather than assignment.
Note: There is no performance difference if the type of x_ is some built-in/intrinsic type, such as int or char* or float. But even in these cases, my personal preference is to set those data members in the initialization list rather than via assignment for consistency. Another symmetry argument in favor of using initialization lists even for built-in/intrinsic types: non-static const and non-static reference data members can't be assigned a value in the constructor, so for symmetry it makes sense to initialize everything in the initialization list.
Now for the exceptions. Every rule has exceptions (hmmm; does "every rule has exceptions" have exceptions? reminds me of Gödel's Incompleteness Theorems), and there are a couple of exceptions to the "use initialization lists" rule. Bottom line is to use common sense: if it's cheaper, better, faster, etc. to not use them, then by all means, don't use them. This might happen when your class has two constructors that need to initialize the this object's data members in different orders. Or it might happen when two data members are self-referential. Or when a data-member needs a reference to the this object, and you want to avoid a compiler warning about using the this keyword prior to the { that begins the constructor's body (when your particular compiler happens to issue that particular warning). Or when you need to do an if/throw test on a variable (parameter, global, etc.) prior to using that variable to initialize one of your this members. This list is not exhaustive; please don't write me asking me to add another "Or when...". The point is simply this: use common sense.
精彩评论