36

The standard and the C++ book say that the default constructor for class type members is called by the implicit generated default constructor, but built-in types are not initialized. However, in this test program I get unexpected results when allocating an object in the heap or when using a temporary object:

#include<iostream>


struct Container
{
    int n;
};

int main()
{
    Container c;
    std::cout << "[STACK] Num: " << c.n << std::endl;

    Container *pc = new Container();
    std::cout << "[HEAP]  Num: " << pc->n << std::endl;
    delete pc;

    Container tc = Container();
    std::cout << "[TEMP]  Num: " << tc.n << std::endl;

}

I get this output:

[STACK] Num: -1079504552
[HEAP]  Num: 0
[TEMP]  Num: 0

Is this some compiler specific behaviour? I don't really intend to rely on this, but I'm curious to know why this happens, specially for the third case.

Jacobo de Vera
  • 1,863
  • 1
  • 16
  • 20
  • possible duplicate of [Default Struct Initialization in C++](http://stackoverflow.com/questions/2951586/default-struct-initialization-in-c) – sharptooth Feb 08 '11 at 12:13
  • 1
    my guess would be that the heap itself is initialized with zeros – davka Feb 08 '11 at 12:14
  • 1
    I thought the same about the heap case, maybe it was just zeroes by chance, but I find the temporary object case surprising. – Jacobo de Vera Feb 08 '11 at 12:16
  • see the link posted by @sharptooth, it has the explanation of the temp case – davka Feb 08 '11 at 12:30
  • Most operating systems as a security measure will zero out pages of memory before handling them to a process (to avoid a process being able to inspect other processes' memory). The result of this is that more often than not, a simple test case that just mallocs will get *initialized* memory (not really, but it will look like), while the stack has more chances of being modified by previous function calls. – David Rodríguez - dribeas Feb 09 '11 at 14:29

2 Answers2

51

It's expected behaviour. There are two concepts, "default initialization" and "value initialization". If you don't mention any initializer, the object is "default initialized", while if you do mention it, even as () for default constructor, the object is "value initialized". When constructor is defined, both cases call default constructor. But for built-in types, "value initialization" zeroes the memory whereas "default initialization" does not.

So when you initialize:

Type x;

it will call default constructor if one is provided, but primitive types will be uninitialized. However when you mention an initializer, e.g.

Type x = {}; // only works for struct/class without constructor
Type x = Type();
Type x{}; // C++11 only

a primitive type (or primitive members of a structure) will be VALUE-initialized.

Similarly for:

struct X { int x; X(); };

if you define the constructor

X::X() {}

the x member will be uninitialized, but if you define the constructor

X::X() : x() {}

it will be VALUE-initialized. That applies to new as well, so

new int;

should give you uninitialized memory, but

new int();

should give you memory initialized to zero. Unfortunately the syntax:

Type x();

is not allowed due to grammar ambiguity and

Type x = Type();

is obliged to call default constructor followed by copy-constructor if they are both specified and non-inlineable.

C++11 introduces new syntax,

Type x{};

which is usable for both cases. If you are still stuck with older standard, that's why there is Boost.ValueInitialized, so you can properly initialize instance of template argument.

More detailed discussion can be found e.g. in Boost.ValueInitialized documentation.

Jan Hudec
  • 73,652
  • 13
  • 125
  • 172
  • great answer but what about std::make_unique(), will it value initialize the variable ? – tommyk Nov 09 '16 at 10:49
  • @tommyk, `std::make_unique` didn't exist when the answer was written. But since it's defined as `make_unique(Args&&... args)` without zero-argument overload it calls `T(args)` even if `args` is actually empty and that causes value initialization. – Jan Hudec Apr 13 '22 at 11:05
19

The short answer is: the empty parenthesis perform value initialization.

When you say Container *pc = new Container; instead, you will observe different behavior.

Community
  • 1
  • 1
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • @FredOverflow, Right, but my gcc initializing POD types to 0 with or without parenthesis. – UmmaGumma Feb 08 '11 at 12:22
  • 1
    @Ashot: That's coincidence, happens on my machine, too. Just create a couple of more on the heap without the parenthesis, and you'll get different values. – fredoverflow Feb 08 '11 at 12:26
  • 1
    @FredOverflow Right after allocating lot of memory I finally get nonzero data :) Thanks. – UmmaGumma Feb 08 '11 at 12:35
  • 1
    @Ashot Martirosyan: You can perform this test to see whether it is actually initializing or not: `struct test { int x; }; void foo() { test t; std::cout << t.x << std::endl; ++t.x; } int main() { foo(); foo(); foo(); }` The common pattern (if the compiler does not inline) is that `t` will be created in the same memory position in the consecutive calls, the first time it will most probably be `0`, but then it will change. If the code does initialize, you will consistently get `0` in all calls. – David Rodríguez - dribeas Feb 08 '11 at 13:22
  • @David Rodríguez - dribeas : This code always(on my all tests) printing 0 :) Don't forget, that calling foo() also use memory, so actually your `t` objects aren't placed in the same place in stack:). – UmmaGumma Feb 08 '11 at 13:33
  • 1
    @Ashot Martirosyan: If each call to a method requires extra memory, you would get stack overflows too easily: `for (int i =0; i < 1000000000; ++i ) { foo(); }`. Each call to `foo()` will get its own bite out of the stack, and it will be released when the function returns (usually the caller, but that is an implementation detail). Consecutive calls to the same function should reuse the same piece of the stack. Did you add any code to the test? What compiler are you using? The code above shows the expected results in g++. – David Rodríguez - dribeas Feb 09 '11 at 10:58
  • @David Rodríguez - dribeas I think you don't understand what I say. Compiler will generate one function call code(I mean allocated memory) in loop. While calling function 2 times without loop(`foo();foo()`) will generate 2 function calls.So your new code isn't equal to previous one. This one will work way you expect, while previous one will not. I'm using g++ too. – UmmaGumma Feb 09 '11 at 17:35