4

I recently ran into a problem with one of my classes because I hadn't set a pointer to NULL in my constructor initialiser list and so it contained rubbish when I ran the program.

However, whilst I know that instances of a built-in type declared on the stack but not initialised will contain random values, I was pretty sure I'd read somewhere that as class members not placed explicitly in the constructor initialiser list would have their default constructors called, for built-in types this would occur too, inserting code like a pseudo-constructor that will on most platforms, set them to zero I also thought I'd read in 'Thinking in C++' somewhere that under certain circumstances before an object is constructed its memory will be zeroed-out, however I appear to be wrong on both occasions.

Please could anyone confirm for me,
a) Does initialisation of members of a built-in type have anything to do with whether a user-defined constructor is defined or not,
b) do members of a built-in type always need to be initialised manually, and
c) are there any circumstances under which an object's storage is zeroed-out before the constructor is called?

Also, in researching this, I have seen the terms 'default-initialised' and 'zero-initialised' used - is there a difference between saying:

T a;

and

T a();

? I thought that the first form was just used to prevent ambiguity when the second may be taken by the compiler as a function declaration.

Thank you very much for your time,

stellarpower

user2672165
  • 2,986
  • 19
  • 27
stellarpower
  • 332
  • 3
  • 13
  • 1
    The default constructor will be called on objects, but simple built-in types (ints/floats/etc) are not initialized. – Buddy Sep 02 '15 at 17:50
  • 2
    this is a duplicate about "value initialization" – user3528438 Sep 02 '15 at 17:54
  • oops, `T a;` is a declaration of a variable `a` of type `T`. `T a();` is a definition of a prototype of a function called `a`, having an empty list of parameters and returning a value of type `T`. By the way, if you use just `T a;` and `T` class has a default constructor, it will be called, but a pointer is not a class, it doesn't have a default constructor, so you had better to initialize it. – Luis Colorado Sep 04 '15 at 06:57

3 Answers3

13

First let's go over some examples and the correct terminology.

T a1;            // default initialization
T a2{};          // value initialization
T();             // also value initialization
new T;           // default initialization
new T();         // value initialization
new T{};         // also value initialization
class C1 {
    C1() {}
    T x;
};               // no initializer for C1::x; default-initialized
class C2 {
    T x;
};               // implicit default constructor default-initializes C2::x
class C3 {
    C3() : x() {}
    T x;
};               // C3::x will be value-initialized.
class C4 {
    C4() : x{} {}
    T x;
};               // C4::x will also be value-initialized.
// DANGER
T a();           // declares a function; not value initialization (quirk of C++)

Generally, the rule is that when there is no initializer, it is default initialization, and when the initializer is () or {}, it is value initialization. Note that there is an exception for statics and thread-locals, which I'll discuss later.

For an integer or floating-point type, value initialization sets it to 0. For a pointer type, value initialization sets it to null. Default initialization does nothing for scalar types. Therefore, if an object of scalar type only receives default initialization, then it has indeterminate value.

a) Does initialisation of members of a built-in type have anything to do with wheher a user-defined constructor is defined or not,

The default constructor for a class default-initializes members. A member is also default-initialized when no mem-initializer is explicitly provided for it. The examples C1 and C2 illustrate this. However, note that when a class type is value-initialized, and the class's default constructor is either implicitly defined or explicitly defaulted, the class's members will be zeroed out. This zeroing out occurs only in this case, and doesn't occur for a user-provided default constructor. So the answer to your question is "yes" in this sense.

C1 y1;   // y1 is default-initialized; y1.x is indeterminate
C1 y2{}; // y2 is value-initialized;   y2.x is indeterminate
C2 y3;   // y3 is default-initialized; y3.x is indeterminate
C2 y4{}; // y4 is value-initialized;   y4.x is set to 0
C3 y5;   // y5 is default-initialized; y5.x is set to 0
C3 y6{}; // y6 is value-initialized;   y6.x is set to 0

b) do members of a built-in type always need to be initialised manually, and c) are there any circumstances under which an object's storage is zeroed-out before the constructor is called?

I assume you mean "class members with built-in type". I covered a case above in which they are automatically initialized to 0: where the class object is value-initialized and its constructor is not user-provided (or deleted). Another case is when the class object has static or thread-local storage duration. In this case, the members will also be zeroed out at the very beginning, so there is no chance of them ending up with indeterminate value.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • Thanks, you've made a complex topic really clear! So, if I've understood you correctly, with class types, the members are automagically value-initialised if the object is value-initialised and the class doesn't define a custom default constructor as in the case of C2. It's helped a lot to be thinking of default initialisation, doing nothing for built-ins, as separate from value initialisation, which defaults to 0/NULL for builtins. I think now what I was remembering was the location of the static members being cleared out before initialisation takes place. Thanks a lot! – stellarpower Sep 02 '15 at 19:24
1

The term you are looking for is default initialized. Any instance data member will be default initialized if it is not explicitly initialized in the body of the constructor. What this means depends on whether the data member is a primitive data type or not and where the object instantiation takes place. In case of data members of non-primitive data types, the default constructor will be used (or the compiler will complain if there is no default constructor). In case of data members of primitive data types this means that they will not be initialized if the instantiation takes place on the stack or on the heap. If it is a global or a static variable however, the data members will be set to 0.

By the way, when you write

T a;

then you define a variable a of type T. When you write

T a();

then you declare a function a() which returns T by value.

shargors
  • 2,147
  • 1
  • 15
  • 21
  • `If it is a global or a static variable however, the data members will be set to 0` where is that called out? – NathanOliver Sep 02 '15 at 17:58
  • @NathanOliver - It's in the section [basic.start.init] (section 3.6.2 in my draft). *"Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5) before any other initialization takes place."* Often this is done automatically by the OS to hide data from earlier programs running in the same memory area. – Bo Persson Sep 02 '15 at 18:06
  • @BoPersson Thank you! I had no idea where it said so in the standard. – shargors Sep 02 '15 at 18:12
1

One way to default initialize all member variables without calling a constructor would be if you have a POD type. For e.g. this is a POD

class Foo {
    int num1;
    double num2;
    bool b;
    int *pnum;
};

and if you do

Foo foo = {};

everything will be zero-initialized (b will be false, pnum NULL etc) without calling the (default) constructor.

So

a) a POD type must not have a user defined constructor so in that case yes

b) if you have a POD you don't have to initialize them manually

c) if you have a POD type you can zero-initialize without calling a constructor.

Another way to create a class instance and zero-initialize without calling a constructor would be to allocate the required memory with calloc and cast the pointer to the required type. You should avoid that unless absolutely necessary!

Community
  • 1
  • 1
Manos Nikolaidis
  • 21,608
  • 12
  • 74
  • 82
  • Manos, default constructor will still be called in Foo foo = {}; – shargors Sep 02 '15 at 18:21
  • I am under the impression that in "aggregate initialization" the default constructor is not used. For e.g. this [answer](http://stackoverflow.com/a/2418195/1413133). If I am wrong, by all means enlighten me! – Manos Nikolaidis Sep 02 '15 at 18:38
  • Thanks, I'd forgotten about PODS. So, this corresponds to Brian's class C2 above, and in this case there are no non-POD-compatible types in the class? I certainly won't be aiming to use calloc and casting! :p – stellarpower Sep 02 '15 at 19:27
  • Yes that corresponds to C2 in Brian's excellent answer. A POD must only contain primitives or other PODs to be a POD. I have only used the calloc and casting thing for low level hardware drivers where I couldn't avoid it. Shot myself in the foot, more than once :-( – Manos Nikolaidis Sep 02 '15 at 20:36