questions about object initialization, default-init and value-init

J

Jess

Hello,

I tried several books to find out the details of object
initialization. Unfortunately, I'm still confused by two specific
concepts, namely default-initialization and value-initialization. I
think default-init calls default constructor for class objects and
sets garbage values to PODs. Value-init also calls default
constructor for class objects and sets 0s to POD types. This is what
I've learned from the books (especially Accelerated C++). However, I
don't think my understanding about these concepts is adequate and I'm
not sure under what situations each of these two init methods is
used. Therefore, I'm posting my questions below along with what I
understand about these two init methods. I'd appreciate it if someone
could answer my questions and point out my mistakes. Thanks.

1. Default initialization is used when we create an object using the
form "T t", where T can be a POD type or class type. If this happens,
the following occurs:
a. if T is POD, then "t" is garbage value

b. if T is class, then it's default constructor is invoked. If
there's no default contructor, the compiler will synthesize one.
(i). If there's a default constructor, then all T's POD member data is
initialized according to the constructor initializer list; any POD
member not mentioned in the initializer list gets default-init, with
garbage value. If T has a class-typed member (say of type A), then
A's default constructor is called and if A has no default constructor,
then the synthesized constructor will be called. FInally, the
constructor's body is entered.
(ii). If there is no default constructor, then the synthesized
constructor will be called. All POD member gets garbage data and
class members have their default constructor (or synthesized default
constructors) invoked.

Is this what happens to default-init? Moreover, to init POD member in
the constructor initializer, I can do things like "x(10)" and also
"x()" where "x" is an int, is this right? If "x()" is used, then "x"
gets value-init to 0?

2. Value-initialization happens if a constructor is explicitly invoked
like "T t(arg)", its copy constructor is invoked. For a class type
object:
a. if it's created by invoking a constructor like "T t(arg)", then all
its POD member gets initialized according to the constructor
initializer; any POD member not mentioned gets value-init to 0. But
what happens to those class-typed members? Are their default
constructors (or synthesized one) called? I think after all these are
done, the constructor's body is entered.

b. if the T object is created through copy constructor, then the
values of the existing objects are copied into the new object.
However, what if we have an example like "B b = B()"? what's the value
of the tmp object created by "B()"? Is it the result of default-init
or value-init?

3. To value-init a POD object, I think the only way is to do something
like:

int n = int();
is this right?

4. The book Accelerated C++ also says that if an object is used to
init a container element, either as a side effect of adding new
element to a "map", or as the elements of a container defined to have
a given size, then the members will be value-initialized. I checked
it through an example,

struct A{
int x;

};

int main(){
A a[10];
return 0;

};

Then I found out each element in a has it's "x" value as 0. I think
this is what the author of the book meant. Then I tried another
example:

int main(){
int b[10];
return 0;

}

This time, each element in b has garbage value. Isn't each member of
b supposed to be value-init to 0?

Thanks a lot
Jess
 
J

James Kanze

I tried several books to find out the details of object
initialization. Unfortunately, I'm still confused by two specific
concepts, namely default-initialization and value-initialization.

Don't feel bad. The definitions for these two have changes
several times. The simplest solution for the everyday use is to
consider them synonyms, and not worry about the subtle
differences. If you design your classes correctly, they
shouldn't really be relevant anyway.
I think default-init calls default constructor for class
objects and sets garbage values to PODs.

No. At least, not as it is defined in the standard.
Value-init also calls default
constructor for class objects and sets 0s to POD types. This is what
I've learned from the books (especially Accelerated C++).

I think that Andy wrote the book before the term
value-initialization was added to the standard; he probably was
defining some terms of his own, just to try to avoid confusion.
Roughly speaking:

-- If an object has a user defined constructor---any user
defined constructor, it is impossible to create an instance
of the object without calling a constructor. Period.
Whether the object is a sub-object, or stands alone. You
don't have to worry about different types of initialization
in this case, since they're all the same.

-- For everything but POD types, you should ensure that the
object has a constructor which initializes everything. Once
you have to provide a constructor, there's no point in doing
a half way job. So again, you don't have to worry about the
different types of initialization.

-- For PODs, there are three cases:

. objects with static lifetime, and objects initialized by
an empty (), are zero initialized,

. objects with a explicit initializer are always
completely initialized---fields with missing initializer
values will be zero initialized, and

. everything else is uninitialized, random garbage.

The different types of initialization only become relevant when
you have constructors which only do a half-way job.
However, I don't think my understanding about these concepts
is adequate and I'm not sure under what situations each of
these two init methods is used. Therefore, I'm posting my
questions below along with what I understand about these two
init methods. I'd appreciate it if someone could answer my
questions and point out my mistakes. Thanks.
1. Default initialization is used when we create an object using the
form "T t", where T can be a POD type or class type.

No. According to the standard (ISO 14882:1998), default
initialization is used in this case if the object is of non-POD
class type, otherwise, no initialization is done.
If this happens, the following occurs:
a. if T is POD, then "t" is garbage value
b. if T is class, then it's default constructor is invoked. If
there's no default contructor, the compiler will synthesize one.
(i). If there's a default constructor, then all T's POD member data is
initialized according to the constructor initializer list; any POD
member not mentioned in the initializer list gets default-init, with
garbage value. If T has a class-typed member (say of type A), then
A's default constructor is called and if A has no default constructor,
then the synthesized constructor will be called. FInally, the
constructor's body is entered.
(ii). If there is no default constructor, then the synthesized
constructor will be called. All POD member gets garbage data and
class members have their default constructor (or synthesized default
constructors) invoked.

That's corresponds pretty well to the effect. With one
exception: if there is any user defined constructor, the
compiler will not synthesize a default constructor. If there is
one or more user defined constructors, but no user defined
default constructor, you cannot create an instance of the class
without specifying some arguments for initialization.
Is this what happens to default-init?

It's what happens "by default", when initializing an object with
no explicit initialization list, provided the object doesn't
have static storage duration.
Moreover, to init POD member in
the constructor initializer, I can do things like "x(10)" and also
"x()" where "x" is an int, is this right? If "x()" is used, then "x"
gets value-init to 0?

Roughly speaking, X() initializes the object with the value it
would have as an object with static storage duration; i.e. as if
it were zero initialized, and then any non-trivial constructor
were called on it.
2. Value-initialization happens if a constructor is explicitly invoked
like "T t(arg)", its copy constructor is invoked. For a class type
object:

Not in the standard. That's explicit initialization. It can
only be used for a class type, and overload resolution is used
on the argument list to decide which constructor to call.
a. if it's created by invoking a constructor like "T t(arg)", then all
its POD member gets initialized according to the constructor
initializer; any POD member not mentioned gets value-init to 0.

No. If the object has static storage duration, it will be
initially zero initialized, and then the constructor will be
called, otherwise, the constructor will be called directly.

If the object doesn't have class type, then this is the
equivalent of:
T t = arg ;
But
what happens to those class-typed members? Are their default
constructors (or synthesized one) called? I think after all these are
done, the constructor's body is entered.

All constructors systematically call the constructors of all
sub-objects, if those sub-objects have a non-trivial
constructor.
b. if the T object is created through copy constructor, then the
values of the existing objects are copied into the new object.
However, what if we have an example like "B b = B()"? what's the value
of the tmp object created by "B()"? Is it the result of default-init
or value-init?

Yes:). According to ISO 14882::1998, it is default-init;
according to the latest draft, it is value-init, but if you are
writing reasonable code, it doesn't matter.
3. To value-init a POD object, I think the only way is to do something
like:
int n = int();
is this right?

value-initialization and default-initialization are the same
thing for POD objects.
4. The book Accelerated C++ also says that if an object is used to
init a container element, either as a side effect of adding new
element to a "map", or as the elements of a container defined to have
a given size, then the members will be value-initialized. I checked
it through an example,
struct A{
int x;
};
int main(){
A a[10];

Attention: this is NOT a container, at least not in the sense
Accelerated C++ means in tha above statement.
return 0;
};
Then I found out each element in a has it's "x" value as 0.

Or something else. The initial value is not specified, and any
attempt to read it is undefined behavior, so you could end up
with everything 0. You could also end up with values that cause
a core dump when read, if such exist on the machine in question.
I think
this is what the author of the book meant. Then I tried another
example:
int main(){
int b[10];
return 0;
}

You're reading Accelerated C++, right? Doesn't that book
normally use std::vector said:
This time, each element in b has garbage value. Isn't each member of
b supposed to be value-init to 0?

There's no difference in your two examples. In both cases, the
objects are simply not initialized.
 
J

Jess

Don't feel bad. The definitions for these two have changes
several times. The simplest solution for the everyday use is to
consider them synonyms, and not worry about the subtle
differences. If you design your classes correctly, they
shouldn't really be relevant anyway.

Thank you so much for your detailed explanations, really helpful to
me. :) I'll make sure to initialize everything, but sometimes I'd
have to build on existing programs written by other people, and
there's no guarantee their programs init every thing properly...
No. If the object has static storage duration, it will be
initially zero initialized, and then the constructor will be
called, otherwise, the constructor will be called directly.

So if T is a class type then "static T t(args)" zero-init t and then
calls the constructor again? Moreover, zero-init "t" will set all POD
members of "t" to zero, is this right?
All constructors systematically call the constructors of all
sub-objects, if those sub-objects have a non-trivial
constructor.

If there's no default constructor, will the compiler call the
synthesized version?
Attention: this is NOT a container, at least not in the sense
Accelerated C++ means in tha above statement.

I see, so container really has to be the library-defined container,
rather than the primitive arrays, which are kind of container (in the
sense that they store objects).
You're reading Accelerated C++, right? Doesn't that book
normally use std::vector<int> for arrays?

Yes, it uses vectors as arrays most of the time, until it reaches the
pointers-arrays chapter. This reminds me of another question. The
book says if I use "new T[n]", then we must be able to default-init
"T", otherwise the compiler will reject it. I tried both POD types
(int) and also object types, and neither was rejected by the
compiler. What does the author really mean? Moreover, if "T" is
char, then the array looks like a sequence of '\0', and if T is int,
then it's a sequence of 0. So, I guess it does zero-init to all the
objects in the array...

Thanks!
Jess
 
J

James Kanze

Thank you so much for your detailed explanations, really helpful to
me. :) I'll make sure to initialize everything, but sometimes I'd
have to build on existing programs written by other people, and
there's no guarantee their programs init every thing properly...

I know the feeling:). I often have to build on code which has
a lot of undocumented preconditions too.
So if T is a class type then "static T t(args)" zero-init t and then
calls the constructor again? Moreover, zero-init "t" will set all POD
members of "t" to zero, is this right?

Yup.

Generally, it's not something you can count on, because when you
write the class, you cannot guarantee that all instances will
have static lifetime. There are a few exceptions, however, and
I've written one or two classes (in over 15 years of C++) which
depended on zero initialization. I'd consider such things to be
"tricky code", however, only to be used if absolutely necessary,
and then only be experienced programmers.
If there's no default constructor, will the compiler call the
synthesized version?

If there is one. (If there isn't any code which requires the
default constructor is illegal.) There are two distinctions
between constructors: trivial vs. non-trivial, and user defined
vs. implicit. All user defined constructors are considered
non-trivial, but implicitly defined constructors can also be
non-trivial: if the class has a sub-component with a non-trivial
constructor, or virtual functions, or a virtual base, the
compiler generated constructor is also non-trivial. A class
with a non-trivial constructor is not a POD. Generally
speaking, if a class is not a POD, the constructor is
responsible for all initializations. If the compiler generated
default constructor doesn't do what is wanted, then the author
of the class has the responsibility of writing one.

In practice, I think that there is also a distinction between
"agglomerates" and other classes. If a type (class or C-style
array) is an agglomerate, you can (and generally should)
initialize it using the "{...}" syntax. If a class isn't an
agglomerate, the only initialization possible is through the
constructors; if they don't do an adequate job, the class is
unusable.
I see, so container really has to be the library-defined container,
rather than the primitive arrays, which are kind of container (in the
sense that they store objects).

Everything is a kind of container, if you want, since everything
contains at least one object (and everything contains some
number of bits). But the usual meaning of "container" in C++
doesn't include C style arrays, although you're right that it
could, logically.
Yes, it uses vectors as arrays most of the time, until it reaches the
pointers-arrays chapter. This reminds me of another question. The
book says if I use "new T[n]", then we must be able to default-init
"T", otherwise the compiler will reject it. I tried both POD types
(int) and also object types, and neither was rejected by the
compiler. What does the author really mean?

I'm not sure, but I suspect that he's using default initialize
in a more general sense: using the strict definition in the
standard, "new T[n]" does not default initialize the objects.
If the object type T is not a POD, it calls the default
constructor (which must be accessible); otherwise, the object is
not initialized (and type T may not be const qualified).
Moreover, if "T" is char, then the array looks like a sequence
of '\0', and if T is int, then it's a sequence of 0. So, I
guess it does zero-init to all the objects in the array...

Not at all.

Be very careful about what you see in tests. On a lot of
systems (most?), memory just received from the OS will be
zeroed, for security reasons. In most cases, operator new (and
allocation of local variables) just hands out the memory, with
whatever values it happens to have. If you write a small test,
which just does one allocation, then looks at the values,
there's a good chance that they are 0, the same as if you only
call one function, and look at a few local variables in it. In
a real application, on the other hand, memory is constantly
being allocated and freed, and in most implementations, what you
get when the memory is allocated is whatever whoever used it
before left there---just random junk, for all intents and
purposes. (When developing code, I use a special operator new,
which fills the memory with an easily identifiable non-zero
value, so that if I forget initialization, the program will
probably fail immediately, and looking at the objects, it will
be recognizable that there was no initialization.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top