2

These are my class definitions:

class Foo{
    int _ent;
public:
    void printEnt() const{cout << _ent << ' ';}
};

class Bar{
    Foo _foo;
public:
    void printEnt() const{_foo.printEnt();}
};

And this is my test code:

char* buf = new char[sizeof(Foo) + sizeof(Foo) + sizeof(Bar)];

fill(buf, buf + sizeof(Foo) + sizeof(Foo) + sizeof(Bar), 'J');

cout << ((int*)buf)[0] << ' ' << ((int*)buf)[1] << ' ' << ((int*)buf)[2] << endl;

Foo* first = new (buf) Foo;
Foo* second = new (buf + sizeof(Foo)) Foo();
Bar* third = new (buf + sizeof(Foo) * 2) Bar;

first->printEnt(); second->printEnt(); third->printEnt();

My output is:

1246382666 1246382666 1246382666
1246382666 0 1246382666

But if I add a public default ctor to Foo: Foo() : _ent(0) {}

My output becomes:

1246382666 1246382666 1246382666
0 0 0

Is this correct behavior? Should adding my own default ctor remove the possibility of default initialization?

I'm running this code on gcc 4.8.1 if it matters. The results should be dependable because I'm running in debug and asserting: assert(sizeof(Foo) == sizeof(int) && sizeof(Bar) == sizeof(int));

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • If anyone is interested I started trying to understand the difference between value-initialization and default-initialization due to this in response to: http://stackoverflow.com/a/27443703/2642059 – Jonathan Mee Dec 16 '14 at 13:17
  • `(sizeof(int) / sizeof(char))` doesn't make much sense. `sizeof(char)` is always one. You also ignore alignment and padding. – pmr Dec 16 '14 at 13:20
  • Why not just `buf + sizeof(Foo)` ? – Borgleader Dec 16 '14 at 13:21
  • @pmr It's been too long since I did pointer arithmetic and I was scared. If you feel like cleaning up the code, I approve. What do you mean by: "You also ignore alignment and padding"? – Jonathan Mee Dec 16 '14 at 13:22
  • You simply ignore those language rules. The code is full of undefined behavior and the output could very well be anything. – pmr Dec 16 '14 at 13:24
  • @Borgleader Yes, yes that's better. Feel free to edit. I just knew this worked. The point of the question wasn't supposed to be pointer arithmetic, but I suppose that is distracting. – Jonathan Mee Dec 16 '14 at 13:24
  • @pmr I'm not sure what "alignment and padding rules" are... The reason I used placement new was to *avoid* undefined behavior. If I've done something to cause undefined behavior please help me understand what it was? – Jonathan Mee Dec 16 '14 at 13:29
  • Padding: the size of a structure is not equal to the size of all it's members. There could be padding depending on the architecture. Thus it's possible you override parts of objects when you calculate the buffer offsets with `buf + sizeof(int)/sizeof(char)`. – pmr Dec 16 '14 at 13:45
  • 1
    @pmr That's not true. The pointer returned by `new char[]` (or `new unsigned char[]`) is guaranteed to be sufficiently aligned for an object of any type. – James Kanze Dec 16 '14 at 13:51
  • @pmr I haven't heard of compilers that allocate more space for classes than the sum of their member variables, but I added an `assert` to validate that mine is not. You agree this will lock in consistent behavior, right? – Jonathan Mee Dec 16 '14 at 13:52
  • 2
    @JonathanMee I haven't heard of a compiler that doesn't. If the class has virtual functions, _all_ of the compilers I know will allocate an extra pointer (or sometimes two, if there is virtual inheritance). And all of the compilers I know enforce alignment, so something like `struct { double d; char c; }` will have a size greater than `sizeof(double) + sizeof(char)`. (And of course, an empty class will still have a size of at least 1.) – James Kanze Dec 16 '14 at 14:10
  • @JamesKanze Thanks. This is why I ask questions on StackOverflow. I feel like I learn even more than the answer to whatever question I asked. – Jonathan Mee Dec 16 '14 at 14:13

3 Answers3

3

Once you provide a constructor for a type, it will always be invoked, both for default initialization and for value initialization. It's a fundamental principle of the language. So once you define Foo::Foo(), it will be called any time you construct a Foo; if there is a default constructor, it will be invoked, even in the case of default initialization. So the behavior you are seeing is correct.

EDIT:

Default initialization is explained §8.5/7, in particular:

To default-initialize an object of type T means:

— if T is a (possibly cv-qualified) class type (Clause 9), the default constructor (12.1) for T is called [...]

In your case, you'll probably also want to look at how the compiler generates a default constructor if none is provided, §12.1/4; in particular, the generated default constructor invokes the default constructor of any base classes or members.

Value initialization is in §8.5/8. It is basically default initialization preceded by zero initialization, so that default initialization that doesn't do anything still finds everything zero initialized.

More fundamentally, however: in this case, a very fundamental principle of C++ is involved, dating to long before the first standard: if you provide a constructor for an object, it will be used. Without doing all sorts of strange pointer casts, it is impossible to get an object without it being properly constructed. The standard describes how this occurs, and covers a lot of other special cases, but the basic principle has been there from the start (and any proposal which would cause it not to be respected in the standard is bound to fail).

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • This appears to be the correct answer, could you add a source? @juanchopanza linked: http://en.cppreference.com/w/cpp/language/value_initialization but it only addresses: "This is the initialization performed when a variable is constructed with an empty initializer." I'd like to see something that says the default initialization will invoke a user default ctor. – Jonathan Mee Dec 16 '14 at 13:57
  • I found no reference for the fact that value initialization is "basically default initialization preceded by zero initialization". What I find is that, if a class has no user-declared default c-tor, you can use `()` or `{}` to zero-initialize, but if the class declares a default c-tor then that c-tor is called without prior zero-ing. The corollary to this is that if you declare a default c-tor, you must handle all zeroing inside of it. In fact, I found no easy way (i.e. other than `memset()`) to zero-initialize a class before setting a sub-set of the members to some values of choice. – haelix Apr 02 '15 at 11:53
1

Your question has been answered in the C++11 revision of the standard:

class Foo{
    int _ent=0;
public:

    // ...
};

If you then define your own default constructor, the member will still be initialized to its default value, even if your default constructor does not explicitly do so.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • But before my default ctor, why does `new Foo` not initialize `_ent` to 0, but `new Foo()` does initialize `_ent` to 0? It's as though the compiler generated default constructor had some magic in it to know whether I'd explicitly called it. – Jonathan Mee Dec 16 '14 at 13:33
  • 1
    @JonathanMee Because the C++ language specifies that behaviour. `new Foo()` value-initializes the `Foo` object. `new Foo` default-initializes it. – juanchopanza Dec 16 '14 at 13:39
  • @JonathanMee Compiler generated default constructors don't initialize primitive data types like `int`. It's value could really be anything. – AndyG Dec 16 '14 at 13:39
  • @juanchopanza And I think therein lies my question. *Why does default initialization never happen again after I define my own default ctor?* – Jonathan Mee Dec 16 '14 at 13:41
  • 1
    @JonathanMee It does. Its effect is to call the user-defined default constructor. – juanchopanza Dec 16 '14 at 13:42
  • @AndyG Visual Studio 2013 has a bug where it doesn't initialize, but zero initialization should be zeroing everything. As is illustrated by my output before adding my default ctor. – Jonathan Mee Dec 16 '14 at 13:43
  • @juanchopanza If what you're saying is right, you just answered my question. Default initialization's, "Effect is to call the user-defined default constructor." If you could provide a reference and put that in an answer I'd like to accept it. – Jonathan Mee Dec 16 '14 at 13:45
  • @JonathanMee It's been asked and answered many times before. I'll look for a suitable duplicate. In the meantime, see http://en.cppreference.com/w/cpp/language/value_initialization – juanchopanza Dec 16 '14 at 13:47
0

When you provided a default constructor you no longer get the compiler generated one. Since your default constructor initializes the member to 0 the member will always be 0, this is especially true since the member is private and you have no way to change it.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402