6

Note: I am using the g++ compiler (which is I hear is pretty good and supposed to be pretty close to the standard).


I have the simplest class I could think of:

class BaseClass  {
  public:
    int pub;
};

Then I have three equally simple programs to create BaseClass object(s) and print out the [uninitialized] value of their data.


Case 1

BaseClass B1;
cout<<"B1.pub = "<<B1.pub<<endl;

This prints out:

B1.pub = 1629556548

Which is fine. I actually thought it would get initialized to zero because it is a POD or Plain Old Datatype or something like that, but I guess not? So far so good.


Case 2

BaseClass B1;
cout<<"B1.pub = "<<B1.pub<<endl;
BaseClass B2;
cout<<"B2.pub = "<<B2.pub<<endl;

This prints out:

B1.pub = 1629556548
B2.pub = 0

This is definitely weird. I created two of the same objects the same exact way. One got initialized and the other did not.


Case 3

BaseClass B1;
cout<<"B1.pub = "<<B1.pub<<endl;
BaseClass B2;
cout<<"B2.pub = "<<B2.pub<<endl;
BaseClass* pB3 = new BaseClass;
cout<<"B3.pub = "<<pB3->pub<<endl;

This prints out:

B1.pub = 0
B2.pub = 0
B3.pub = 0

This is the most weird yet. They all get initialized to zero. All I did was add two lines of code and it changed the previous behavior.


So is this just a case of 'uninitialized data leads to unspecified behavior' or is there something more logical going on 'under the hood'?

I really want to understand the default constructor/destructor behavior because I have a feeling that it will be very important for completely understanding the inheritance stuff..

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Jimmy
  • 4,419
  • 6
  • 21
  • 30

7 Answers7

13

So is this just a case of 'uninitialized data leads to unspecified behavior'

Yes...

Sometimes if you call malloc (or new, which calls malloc) you will get data that is filled with zeroes because it is in a fresh page from the kernel. Other times it will be full of junk. If you put something on the stack (i.e., auto storage), you will almost certainly get garbage — but it can be hard to debug, because on your system that garbage might happen to be somewhat predictable. And with objects on the stack, you'll find that changing code in a completely different source file can change the values you see in an uninitialized data structure.

About POD: Whether or not something is POD is really a red herring here. I only explained it because the question mentioned POD, and the conversation derailed from there. The two relevant concepts are storage duration and constructors. POD objects don't have constructors, but not everything without a constructor is POD. (Technically, POD objects don't have non-trivial constructors nor members with non-trivial constructors.)

Storage duration: There are three kinds. Static duration is for globals, automatic is for local variables, and dynamic is for objects on the heap. (This is a simplification and not exactly correct, but you can read the C++ standard yourself if you need something exactly correct.)

Anything with static storage duration gets initialized to zero. So if you make a global instance of BaseClass, then its pub member will be zero (at first). Since you put it on the stack and the heap, this rule does not apply — and you don't do anything else to initialize it, so it is uninitialized. It happens to contain whatever junk was left in memory by the last piece of code to use it.

As a rule, any POD on the heap or the stack will be uninitialized unless you initialize it yourself, and the value will be undefined, possible changing when you recompile or run the program again. As a rule, any global POD will get initialized to zero unless you initialize it to something else.

Detecting uninitialized values: Try using Valgrind's memcheck tool, it will help you find where you use uninitialized values — these are usually errors.

Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • 1
    `BaseClass` looks like a POD to me. No user-defined ctor there. And _all_ objects with static storage duration are zero-initialised, prior to _possible_ value-initialisation/construction. – Lightness Races in Orbit Jul 26 '11 at 02:27
  • It's not "why". It's true, but it's not "why". That the `int` member is not initialised is because it's of a built-in type; this is not the same as being POD. And `BaseClass` being POD _really_ has nothing to do with it. – Lightness Races in Orbit Jul 26 '11 at 02:29
  • I'm sorry, is it not true that because `BaseClass` is POD, its members must be uninitialized if it is placed on the stack? I'm not sure I understand what you're trying to say, and I'm not *that* familiar with the standard... – Dietrich Epp Jul 26 '11 at 02:32
  • No, there is no truth in that at all. For any class-type, any non-static data member not explicitly initialised in a ctor member-initialiser is default constructed, unless it is of a built-in type (`int`, `char`, `bool` and friends) in which case it is uninitialised. Whether the encapsulating class-type is POD and how an instance was allocated is not relevant. – Lightness Races in Orbit Jul 26 '11 at 02:32
  • Tomalak: I am sincerely trying to understand what you are saying. Can you give an example of a POD that, when placed on the stack without initialization, initializes its members? Because all I am trying to say is that it doesn't happen. Maybe this would be more appropriate in chat? – Dietrich Epp Jul 26 '11 at 02:43
  • @TomalakGeret'kal let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/1818/discussion-between-dietrich-epp-and-tomalak-geretkal) – Dietrich Epp Jul 26 '11 at 02:43
  • Actually I'm afraid my willingness to spend time on SO doesn't extend to late-night teaching! I can recommend [these books](http://jcatki.no-ip.org/fncpp/Resources), though. – Lightness Races in Orbit Jul 26 '11 at 02:50
  • "Can you give an example of a POD that, when placed on the stack without initialization, initializes its members?" No, because I cannot prove that a data member is initialised unless: it _is_ initialised (i.e. it's not built-in); and it itself has some non-built-in members; or unless it has a constructor into which I can put some debug output... and then the encapsulating class is no longer POD. But, again, automatic storage duration and PODness has _nothing to do with it_: neither of these factors influence non-static data member initialisation. This is clear in the standard. – Lightness Races in Orbit Jul 26 '11 at 02:52
  • @Tomalak: I am somewhat frustrated because I don't feel like my comments are being understood, and I am running out of ways to rephrase it. POD types cannot have non-default constructors -> therefore they do not initialize their members. I am not trying to make the arrow point the other way. – Dietrich Epp Jul 26 '11 at 03:18
  • "dynamic is for objects on the heap" <-- this is misleading. The two are not causally related. – Lightness Races in Orbit Jul 26 '11 at 03:19
  • "POD types cannot have non-default constructors -> therefore they do not initialize their members" <-- the "therefore" here is _wrong_. (You mean _user-defined_, not "non-default"). A non-user-defined default constructor will still default-construct any member that's not of a built-in type. It's difficult to prove this because to make use of this fact, you have to break the PODness (at least, I can't think of any examples off the top of my head). But the causality you infer does not exist. // I don't quite know how else to say it. You're not being misunderstood; you've been misinformed. :) – Lightness Races in Orbit Jul 26 '11 at 03:20
  • @Tomalak: What I am trying to say here is that you have to break the PODness to get initialization, if the object is not otherwize initialized and has non-static duration. I am not really trying to give a rigorous proof of that fact. – Dietrich Epp Jul 26 '11 at 03:38
2

In all three cases, those POD objects could have indeterminate values.

POD objects without any initializer will NOT be initialized by default value. They just contain garbage..

From Standard 8.5 Initializers,

"If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or array thereof), the object shall be default-initialized; if the object is of const-qualified type, the underlying class type shall have a user-declared default constructor. Otherwise, if no initializer is specified for a nonstatic object, the object and its subobjects, if any, have an indeterminate initial value; if the object or any of its subobjects are of const-qualified type, the program is ill-formed."

You can zero initialize all the members of a POD struct like this,

BaseClass object={0};
Destructor
  • 14,123
  • 11
  • 61
  • 126
Eric Z
  • 14,327
  • 7
  • 45
  • 69
2

It depends how you declare them:

// Assuming the POD type's
class BaseClass
{
  public:
    int pub;
};

Static Storage Duration Objects

These objects are always zero initialized.

// Static storage duration objects:
// PODS are zero initialized.

BaseClass    global; // Zero initialized pub = 0

void plop()
{
    static BaseClass functionStatic;   // Zero initialized.
}

Automatic/Dynamic Storage Duration Objects

These objects may by default or zero initialized depending on how you declare them

void plop1()
{
    // Dynamic
    BaseClass*  dynaObj1   = new BaseClass;   // Default initialized (does nothing)
    BaseClass*  dynaObj2   = new BaseClass(); // Zero Initialized

    // Automatic
    BaseClass   autoObj1;                     // Default initialized (does nothing)
    BaseClass   autoObj2   =     BaseClass(); // Zero Initialized

    // Notice that zero initialization of an automatic object is not the same
    // as the zero initialization of a dynamic object this is because of the most
    // vexing parse problem

    BaseClass    autoObj3(); // Unfortunately not a zero initialized object.
                             // Its a forward declaration of a function.
}

I use the term 'Zero-Initialized'/'Default-Initialization' but technically slightly more complex. 'Default-Initialization' will becomes 'no-initialization' of the pub member. While the () invokes 'Value-Initialization' that becomes 'Zero-Initialization' of the pub member.

Note: As BaseClass is a POD this class behaves just like the builtin types. If you swap BaseClass for any of the standard type the behavior is the same.

Martin York
  • 257,169
  • 86
  • 333
  • 562
  • Thank you, that is another question I had: What is the difference between: BaseClass pB = new BaseClass; and BaseClass pB = new BaseClass(); ? They both compile and result in Case 3 behavior. – Jimmy Jul 26 '11 at 04:14
1

The way you wrote your class, the value of pub is undefined and can be anything. If you create a default constructor that will call the default constructor of pub - it will be guaranteed to be zero:

class BaseClass  {
  public:
    BaseClass() : pub() {}; // calling pub() guarantees it to be zero.
    int pub;
};

That would be a much better practice.

littleadv
  • 20,100
  • 2
  • 36
  • 50
  • That's neat. So you are saying if I override the default constructor with an explicit (albeit also quite default) constructor then I will always get my basic datatypes initialized? Is that part of the Standard? – Jimmy Jul 26 '11 at 02:27
  • @Jimmy: You'd be overriding the _synthesised_ default constructor with a _user-defined_ default constructor. You won't get them initialised automatically: notice that @littleadv explicitly initialised `pub` in his ctor's member-initialiser. – Lightness Races in Orbit Jul 26 '11 at 02:30
  • 1
    You deciphered and answered my comment perfectly. – Jimmy Jul 26 '11 at 02:46
0

As a general rule, yes, uninitialized data leads to unspecified behavior. That's why other languages like C# take steps to insure that you don't use uninitialized data.

FishBasketGordo
  • 22,904
  • 4
  • 58
  • 91
  • http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html to see the very rational reasons why C (and it's descendant C++) choose to have uninitialised data and unspecified behavior. – KarlM Jul 26 '11 at 03:45
0

This is why you always, always, always, initialize a classes (or ANY variable) to a stable state instead of relying on the compiler to do it for you; especially since some compilers deliberately fill them with garbage. In fact, the only POD MSVC++ doesn't fill with garbage is bool, they get initialized to true. One would think it'd be safer to init it to false, but that's Microsoft for ya.

Casey
  • 10,297
  • 11
  • 59
  • 88
  • Debug builds through MSVC++ fill with `0xDEADBEEF` (IIRC) which is a "recognisable" value in this representation. Not just for `bool`. This, converted to `bool`, is `true`. Filling with `0` wouldn't be very useful; indeed it would be _dangerously misleading_ if you missed a non-initialisation somewhere, but thought that your integral data were guaranteed to start off life with a value of `0` anyway. – Lightness Races in Orbit Jul 26 '11 at 02:31
0

Case 1

Regardless of whether the encapsulating type is POD, data members of a built-in type are not default initialised by an encapsulating default constructor.

Case 2

No, neither got initialised. The underlying bytes at the memory position of one of them just happened to be 0.

Case 3

Same.

You seem to be expecting some guarantees about the "value" of uninitialised objects, whilst simultaneously professing the understanding that no such value exists.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • It is in my nature to have unrealistic expectations, but I am never particulary devastated when they turn out to be false. I know it is bad practice to leave things uninitialized, but want to learn all the C++ shortcuts available. – Jimmy Jul 26 '11 at 02:47
  • @Jimmy: It was the contradiction I was most interested in. :) – Lightness Races in Orbit Jul 26 '11 at 02:49