2

I would like to know the rule for zeroing-out structs (or classes) that have no default constructor in C++.

In particular, it seems that if stored in the stack (say, as a local variable) they are uninitialized, but if allocated on the heap, they are zero-initialized (tested with GCC 4.9.1). Is this guaranteed to be portable?

Example program:

#include <iostream>
#include <map>
using namespace std;

struct X {
    int i, j, k;
    void show() { cout << i << " " << j << " " << k << endl; }
};

int fib(int i) {
    return (i > 1) ? fib(i-1) + fib(i-2) : 1;
}

int main() {
    map<int, X> m;            
    fib(10);                  // fills the stack with cruft
    X x1;                     // local
    X &x2 = m[1];             // heap-allocated within map
    X *x3 = new X();          // explicitly heap-allocated
    x1.show();  // --> outputs whatever was on the heap in those positions
    x2.show();  // --> outputs 0 0 0 
    x3->show(); // --> outputs 0 0 0     
    return 0;
}

Edited: removed an "or should I just use a constructor" in the bolded part; because what made me ask is that I want to know if it is guaranteed behaviour or not - we can all agree that readable code is better of with explicit constructors.

jruizaranguren
  • 12,679
  • 7
  • 55
  • 73
tucuxi
  • 17,561
  • 2
  • 43
  • 74
  • 1
    I don't know enough to post a fleshed-out answer, but `new X()` will initialize the `X`, while `new X` won't (in the present case where `X` is an aggregate) – Quentin May 06 '15 at 13:08
  • 1
    You should always write a constructor and set the internal variables of a class. Regardless if the compiler already sets them. – maja May 06 '15 at 13:09
  • @Quentin These two statements are the same, but the second one (`new X`) should be prefered – maja May 06 '15 at 13:09
  • 2
    @maja they aren't the same and I would prefer `new X()`. Why do you prefer `new X`? – TartanLlama May 06 '15 at 13:15
  • 2
    @maja they're not the same. See [here](http://en.cppreference.com/w/cpp/language/value_initialization), [here](http://en.cppreference.com/w/cpp/language/default_initialization) and [here](http://en.cppreference.com/w/cpp/language/default_constructor). In this case the `X` is either value-initialized (zeroed), or nothing is done (trivial default constructor). Anyway, initialization rules are a nightmare, so just use an explicit constructor and be done with it... – Quentin May 06 '15 at 13:17
  • @TartanLlama Because you can get problems in situations where a function called `X()` exists. In this case, the compiler would interpret `X()` as a function call, while `new X` is always unambiguous. – maja May 06 '15 at 13:17
  • Wrong person, you answered @TartanLlama ;) – Quentin May 06 '15 at 13:19
  • @Quentin: Then leave it to someone who _does_ know enough! No need to answer/suppose in comments; that's not what they're for. Cheers – Lightness Races in Orbit May 06 '15 at 13:24
  • @maja: Complete nonsense. [Dropping the `()` does not save you from that ambiguity!](http://coliru.stacked-crooked.com/a/6de810f7785a18f5) – Lightness Races in Orbit May 06 '15 at 13:24
  • @Quentin Are we speaking of different things? The code `A* a = new A();` is equivalent to `A* a = new A;`, isn't it? If there is no constructor, the default one ist used - it doesn't matter if you omitted the brackets or not. – maja May 06 '15 at 13:24
  • @maja: What are you talking about? That is not a parse ambiguity in C++. – Puppy May 06 '15 at 13:25
  • @maja: No. No, they are not the same. Hence the question and the answer. – Puppy May 06 '15 at 13:25
  • @maja: No, it is not equivalent. As several people have now explained several times. – Lightness Races in Orbit May 06 '15 at 13:26
  • @LightnessRacesinOrbit In case a function with that name exists, the compiler would abort with an error – maja May 06 '15 at 13:26
  • @maja: Click on the link I gave you. Then read its text. You assert that dropping `()` somehow avoids the "ambiguity"; I demonstrated, with a live example, that it does not. – Lightness Races in Orbit May 06 '15 at 13:26
  • @LightnessRacesinOrbit I knew what's in my comment, and nothing more ;) – Quentin May 06 '15 at 13:29
  • @LightnessRacesinOrbit Hm... I need to check that when I'm at home... seems that I mixed someting up... – maja May 06 '15 at 13:30

3 Answers3

5

It's not the dynamic allocation that's zero-initialising your struct members; it's your syntax:

X* ptr = new X();
//            ^^
// 
// as opposed to just:
X* ptr = new X;

If you want it guaranteed, just keep writing that. :)

An alternative, which meshes well with the automatic-storage-duration equivalent, is to use the newer {} syntax:

X* ptr = new X{};   // dynamic
X  obj{};           // automatic

And objects with static storage duration are always pre-zero-initialised no matter what, so you're covered there by default.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Nice answer; I never thought of calling new without parenthesis to compare. Any ideas regarding why the std::map is ()-initializing my struct? – tucuxi May 06 '15 at 13:17
  • 1
    @tucuxi: Because that's what it does! "`[C++11: 23.4.4.3/1]:` _Effects:_ If there is no key equivalent to `x` in the map, inserts `value_type(x, T())` into the map." The standard _could_ have gone of out its way to avoid this value-initialisation and used default-initialisation instead, but it would have added complexity and probably reduced usefulness. I mean, in most cases, it's more useful to have a safely-readable value than it is to avoid a single write that _may_ be redundant. – Lightness Races in Orbit May 06 '15 at 13:20
  • @tucuxi: Because not doing so is dumb. You just end up with a bunch of worthless values you have to set in any case afterwards. – Puppy May 06 '15 at 13:24
  • @tucuxi I can't find a definitive word on this [insert Standard guru invocation here], but IINM merely taking a reference to an uninitialized variable is UB, and `std::map` must return such a reference. – Quentin May 06 '15 at 13:26
  • @Quentin: Can't say I've ever heard of that and I can't find any evidence of it on a [very] quick standard text scan. Not that it would surprise me terribly. – Lightness Races in Orbit May 06 '15 at 13:28
3

Always use a constructor if you want to give your class's members particular values. That's literally what constructors are for. The only time you don't need to write a constructor that sets the values you want is if the language pre-provides one for you like copy constructor. Default constructor is not on this list for int and other such types so you have to add a constructor to your own type that sets them appropriately.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • 1
    This seems like heavy-handed advice without qualifiers. Adding a user-defined constructor changes the properties of the type in a way that you _may_ not want in some situations. – Lightness Races in Orbit May 06 '15 at 13:14
  • sorry, I edited out the "or should I just use a constructor", because I am really curious about why things happen as they do. Yes, I agree that the code would be better-off with a constructor, but I want to understand why it works this way now – tucuxi May 06 '15 at 13:14
  • @Lightness: Anybody who actually runs into one of those situations and understands what properties are changed and why doesn't need the advice of this answer to know what to do. Not writing constructors when you need them for silly reasons is far more common and much worse than accidentally making your type non-POD or something. – Puppy May 06 '15 at 13:21
  • 1
    Ah, I see: "this answer deliberately omits crucial details because anyone reading it won't understand those details". Academic snobbery at its finest! – Lightness Races in Orbit May 06 '15 at 13:29
0

As long as you don't have any constructors, all members are public, and there is no inheritance involved, your struct/class is likely an aggregate.

To get portable zero-initialization, simply use X x = {}; which performs zero intialization.

Here is the standard quote about aggregates (8.5.1):

An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equal-initializers for non-static data members (9.2), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).

reference: Why can I not brace initialize a struct derived from another struct?

underscore_d
  • 6,309
  • 3
  • 38
  • 64
timato
  • 184
  • 2
  • 12