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));
Your question has been answered in the C++11 revision of the standard:
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.
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.
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 aFoo
; 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:
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).