views:

1154

answers:

8

I'm fairly new to C++, and I'm not sure about this one. Have a look at the following example which sums up my current problem.

class Foo
{
    //stuff
};

class Bar
{
    Foo foo;
};

So Bar constains a full Foo object, not just a reference or pointer. Is this object initialized by its default constructor ? Do I need to explicitly call its constructor, and if so, how and where ?

Thanks.

+1  A: 

So Bar constains a full Foo object, not just a reference or pointer. Is this object initialized by its default constructor?

If Foo has a default ctor, an object of type Foo will be using the default ctor when you create an object of type Bar. Otherwise, you need to call the Foo ctor yourself or your Bar's ctor will make your compiler complain loduly.

E.g:

class Foo {
public:
 Foo(double x) {}
};

class Bar  {
 Foo x;
};

int main() {
 Bar b;
}

The above will have the compiler complain something like:

"In constructor 'Bar::Bar()': Line 5: error: no matching function for call to 'Foo::Foo()'

Do I need to explicitly call its constructor, and if so, how and where ?

Modify the above example as follows:

class Foo {
 public:
  Foo(double x) {} // non-trivial ctor
};

class Bar  {     
 Foo x;
public:
  Bar() : x(42.0) {} // non-default ctor, so public access specifier required
};

int main() {
 Bar b;
}
dirkgently
If Foo does not have any constructor defined (besides possibly the copy constructor) then the compiler will not complain at all but will not call the implicitly defined Foo constructor either. The object will be uninitialized.
David Rodríguez - dribeas
That is what I meant by default ctor.
dirkgently
+8  A: 

It will be initialized by its default constructor. If you want to use a different constructor, you might have something like this:

class Foo
{
    public: 
    Foo(int val) { }
    //stuff
};

class Bar
{
    public:
    Bar() : foo(2) { }

    Foo foo;
};
Frank Schwieterman
Syntax error: you need a colon (:) after the public keyword.
dirkgently
And a semi colon (;) after the closing } of each class. I blame Java.
rq
thanks for the syntax corrections. I blame C#.
Frank Schwieterman
:-) uservoice request: code completion and error highlighting on the markdown box thingy.
rq
The first sentence is wrong. Initialization of Foo is not guaranteed. It depends on how Foo is defined for the given Bar. Read my answer at the end of the page: http://stackoverflow.com/questions/849812/c-construction-of-an-object-inside-a-class/850240#850240
David Rodríguez - dribeas
There is no question about how Foo is defined, its defined in the beginning of this post. Obviously I don't intend for the statement to generalize to any potential implementation of Foo.
Frank Schwieterman
The point here is that the answer only applies with a subset of the possibilities. The fact that Bar does not have a user provided constructor could suggest that Foo does not either, and in that case the answer is wrong. Answering the question without knowing the fact is like trying to answer 'do I have to drive on the left or on the right side of a two way street?'. It depends on unknown factors: 'are you driving in the US or in UK?' providing an uninformed answer 'on the right', without warnings can end up in accidents.
David Rodríguez - dribeas
+2  A: 

If you do not explicitly call a constructor of foo inside of Bar's constructor then the default one will be used. You can control this by explicitly calling the constructor

Bar::Bar() : foo(42) {}

This is of course assuming you add a Foo::Foo(int) to the code :)

JaredPar
42, the ubiqutuous random number. :)
Marcus Lindblom
I see that most of you came up with "42" in your examples. Where does that come from?
ichiban
That is a reference to the H2G2 -- the Answer to the Ultimate Question of Life, the Universe, and Everything. Read: http://en.wikipedia.org/wiki/42_(number)#In_The_Hitchhiker.27s_Guide_to_the_Galaxy. Even better read Douglas Adams.
dirkgently
@ichiban, @dirkgently is correct.
JaredPar
Incorrect: The default constructor of Foo will be called if it is *user defined*. If the user did not provide it, the compiler generated *implicit default constructor* will not be called. You can force the compiler to call it in the initialization list
David Rodríguez - dribeas
+1  A: 

Full object. No, it's default constructed in Bar's default constructor.

Now, if Foo had a constructor that only took, say, an int. You'd need a constructor in Bar to call Foo's constructor, and say what that is:

class Foo {
public:
    Foo(int x) { .... }
};

class Bar {
public:
    Bar() : foo(42) {}

    Foo foo;
};

But if Foo had a default constructor Foo(), the compiler generates Bar's constructor automatically, and that would call Foo's default (i.e. Foo())

Marcus Lindblom
I see that most of you came up with "42" in your examples. Where does that come from?
ichiban
Wont work, all ctors are private by default.
dirkgently
Oops. Fixed. I tend to leave that out when thinking/talking about other concepts.
Marcus Lindblom
@ichiban: Read 'The Hitchhikers Guide to the Galaxy' by Douglas Adams. It's a classic very funny novel that most techy types attach to. (The latter instances in the 5-book series aren't as good as the first ones though..)
Marcus Lindblom
@dirkgently: Compiler-generated ("implicity-declared") functions (default constructor, copy constructor, assignment operator, destructor) are "inline public" by default. Explicitly declared members of a class are private unless otherwise specified.
David Thornley
Again same comment as for the other answers: if Foo does not have a user defined default constructor, unless you explictly call the Foo default constructor from Bar's constructor (initialization list) then Foo will be uninitialized.
David Rodríguez - dribeas
A: 

Unless you specify otherwise, foo is initialized using its default constructor. If you want to use some other constructor, you need to do so in the initializer list for Bar:

Bar::Bar( int baz ) : foo( baz )
{
    // Rest of the code for Bar::Bar( int ) goes here...
}
Naaff
If Foo has a user defined default constructor then it will be called. For a implicitly defined default constructor in Foo, no call will be made in the implicitly defined constructor of Bar.
David Rodríguez - dribeas
A: 

You do not need to call the default contructor explicitly in C++, it will be called for you. If you wanted to call a different contructor, you could do this:

Foo foo(somearg)
Shane C. Mason
If Foo has a user defined default constructor then it will be called. For a implicitly defined default constructor in Foo, no call will be made in the implicitly defined constructor of Bar
David Rodríguez - dribeas
+1  A: 

There are four functions the C++ compiler will generate for each class, if it can, and if you don't provide them: a default constructor, a copy constructor, an assignment operator, and a destructor. In the C++ Standard (chapter 12, "Special Functions"), these are referred to as "implicitly declared" and "implicitly defined". They will have public access.

Don't confuse "implicitly-defined" with "default" in a constructor. The default constructor is the one that can be called without any arguments, if there is one. If you provide no constructor, a default one will be implicitly defined. It will use the default constructors for each base class and data member.

So, what is happening is that class Foo has an implicitly defined default constructor, and Bar (which doesn't seem to have a user-defined constructor) uses its implicitly defined default constructor which calls Foo's default constructor.

If you did want to write a constructor for Bar, you could mention foo in its initializer list, but since you're using the default constructor you don't actually have to specify it.

Remember that, if you do write a constructor for Foo, the compiler will not automatically generate a default constructor, and so you will have to specify one if you need one. Therefore, if you were to put something like Foo(int n); into the definition of Foo, and didn't explicitly write a default constructor (either Foo(); or Foo(int n = 0);), you couldn't have a Bar in its present form, since it couldn't use Foo's default constructor. In this case, you'd have to have a constructor like Bar(int n = 0): foo(n) {} having the Bar constructor initialize the Foo. (Note that Bar(int n = 0) {foo = n;} or the like wouldn't work, since the Bar constructor would first try to initialize foo, and that would fail.)

David Thornley
The implicitly defined constructor in Bar will NOT call an implicitly defined constructor in Foo. If Foo has a user defined constructor then Bar implicitly defined constructor will (this time yes) call the user defined constructor in Foo.This is the most complete answer up to here nonetheless: +1
David Rodríguez - dribeas
Sure about that? According to 12.6.2(4), a nonstatic data member is initialized with its default constructor if not mentioned in the initializer list, and in 8.5(5) the default constructor is called unless it's a POD (Plain Old Data, not conveniently defined in the Standard) type, in which case it's initialized to zero. So, I guess the question is whether Foo is a POD type, which we don't get to see. If it's POD type, it's all zero-initialized; if it has one of the things that mark it as non-POD, it's default-initialized and therefore calls the implicitly defined default constructor.
David Thornley
A: 

Construction is a fairly hard topic in C++. The simple answer is it depends. Whether Foo is initialized or not depends on the definition of Foo itself. About the second question: how to make Bar initialize Foo: initialization lists are the answer.

While general consensus is that Foo will be default initialized by the implicit default constructor (compiler generated), that does not need to hold true.

If Foo does not have a user defined default constructor then Foo will be uninitialized. To be more precise: each member of Bar or Foo lacking a user defined default constructor will be uninitialized by the compiler generated default constructor of Bar:

class Foo {
   int x;
public:
   void dump() { std::cout << x << std::endl; }
   void set() { x = 5; }
};
class Bar {
   Foo x;
public:
   void dump() { x.dump(); }
   void set() { x.set(); } 
};
class Bar2
{
   Foo x;
public:
   Bar2() : Foo() {}
   void dump() { x.dump(); }
   void set() { x.set(); }
};
template <typename T>
void test_internal() {
   T x;
   x.dump();
   x.set();
   x.dump();
}
template <typename T>
void test() {
   test_internal<T>();
   test_internal<T>();
}
int main()
{
   test<Foo>(); // prints ??, 5, 5, 5, where ?? is a random number, possibly 0
   test<Bar>(); // prints ??, 5, 5, 5
   test<Bar2>(); // prints 0, 5, 0, 5
}

Now, if Foo had a user defined constructor then it would be initialized always, regardless of whether Bar has or not a user initialized constructor. If Bar has a user defined constructor that explicitly calls the (possibly implicitly defined) constructor of Foo, then Foo will in fact be initialized. If the initialization list of Bar does not call the Foo constructor then it will be equivalent to the case where Bar had no user defined constructor.

The test code may need some explaining. We are interested on whether the compiler does initialize the variable without the user code actually calling the constructor. We want to test whether the object is initialized or not. Now if we just create an object in a function it might happen to hit a memory position that was untouched and already contains zeroes. We want to differentiate luck from success, so we define a variable in a function and call the function twice. In the first run, it will print the memory contents and force a change. In the second call to the function, as the stack trace is the same, the variable will be held in exactly the same memory position. If it was initialized, it would be set to 0, else it would keep the same value the old variable in exactly the same position had.

In each of the test runs, the first value printed is the initialized value (if it was actually initialized) or the value in that memory position, that in some cases happen to be 0. The second value is just a test token representing the value at the memory position after manually changing it. The third value comes from the second run of the function. If the variable is being initialized it will fall back to 0. If the object is not initialized, its memory will keep the old contents.

David Rodríguez - dribeas