Reputation: 223
Given the following code:
class temp
{
public:
string str;
int num;
};
int main()
{
temp temp1;
temp temp2 = temp();
cout << temp1.str << endl; //Print ""
cout << temp2.str << endl; //Print ""
cout << temp1.num << endl; //Print a rand num
cout << temp2.num << endl; //Print 0
}
What is the difference between default-initialization —
temp temp1;
and copy-initialization with value-initialization
temp temp2 = temp();
Upvotes: 22
Views: 4401
Reputation: 320381
The behavior of your code depends critically on the compiler you are using. More precisely, it depends on which version of language specification your compiler implements.
For C++98 compilers, both declarations have identical effect on the final values of the objects being declared: the str
member should become empty, while the num
members should contain unpredictable value. In both cases the actual initialization is default-initialization performed by a compiler-provided default constructor of class temp
. That default constructor initializes str
, but leaves num
uninitialized.
For C++03 compilers the behavior is different. There's no difference for temp1
object (its num
is still unpredictable). But temp2
initialization is handled differently. In C++03 the ()
initializer triggers the new kind of initialization - so called value-initialization. Value-initialization ignores the compiler-provided default constructor of the top level object, and instead works directly on its subobjects (data members in this case). So the temp2
object is effectively initialized by value-initialization, which also sets the num
member to zero (in addition to initializing str
with an empty string). For this reason, temp2.num
ends up being zero in C++03 compilers.
If in your experiments you observed consistent zero in temp2.num
, it means that your compiler follows the C++03 specification in this respect.
Upvotes: 5
Reputation: 52149
temp temp1;
This calls temp
's default constructor on the instance called temp1
.
temp temp2 = temp();
This calls temp
's default constructor on a temporary object, then calls the compiler-generated copy-constructor on temp2
with the temporary object as the argument (this of course assumes that the compiler doesn't elide copies; it depends on your compiler's optimization settings).
As for why you get different initialized values, section 8.5 of the standard is relevant:
T
means:
T
is a scalar type (3.9), the object is set to the value of 0 (zero) converted to T
;T
is a non-union class type, each nonstatic data member and each base-class subobject is zero-initialized;T
is a union type, the object’s first named data member is zero-initialized;T
is an array type, each element is zero-initialized;T
is a reference type, no initialization is performed.To default-initialize an object of type T
means:
T
is a non-POD class type (clause 9), the default constructor for T
is called (and the initialization is ill-formed if T
has no accessible default constructor);T
is an array type, each element is default-initialized;To value-initialize an object of type T
means:
T
is a class type (clause 9) with a user-declared constructor (12.1), then the default constructor for T
is called (and the initialization is ill-formed if T
has no accessible default constructor);T
is a non-union class type without a user-declared constructor, then every non-static data member and base-class component of T is value-initialized;T
is an array type, then each element is value-initialized;So now that the rules have been laid out, let's see how they apply:
temp temp1;
temp
is a non-POD type (because it has a std::string
member), and since no initializer is specified for temp1
, it will be default-initialized (8.5/9). This calls the default constructor (8.5/5). temp
has an implicit default constructor (12/7) which default-initializes the std::string
member and the int
member isn't initialized at all (12.6.2/4).
temp temp2 = temp();
On the other hand, the temporary temp
object is value-initialized (8.5/7), which value-initializes all data members (8.5/5), which calls the default constructor in the std::string
member and zero-initializes the int
member (8.5/5).
Of course, if you much rather not have to refer to the standard in 5+ different places, just ensure that you explicitly initialize everything (e.g. int i = 0;
or using initializer lists).
Upvotes: 23
Reputation: 131789
temp temp1;
Will create a default initialized temp
object. Since you provided no default constructor for temp
, every member of temp
will be default initialized too. Since std::string
provides a default ctor, it gets initialized correctly and has a well-defined value. The integer, however, gets default initialized, which is implementation defined and normally a random value.
temp temp2 = temp();
This will first create a value initialized temp
object. This is important, because the object itself is value initialized, so are its members. It doesn't matter for the string, as default and value initialization are the same, but it matters for the integer. A value initialized integer has the value 0
.
After that, you just copy over those members into temp2
.
Also, this relevant question might be of interest to you.
Edit: See my comment on @In silico's answer for explanation on why this isn't the case for MSVC. :/
Upvotes: 4