SolarBear
SolarBear

Reputation: 4629

C++ - construction of an object inside a class

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.

Upvotes: 35

Views: 87807

Answers (9)

user6882413
user6882413

Reputation: 341

Constructor meant for setting an initial state of object. In case of inheritance hierarchy, base class objects will be constructed in the order of inheritance hierarchy (IS-A relation in OO terminology) followed by derived class objects.

Similarly when an object is embedded in another object (HAS-A relation in OO terms or containment), constructors of embedded object are called in the order of their declaration.

The compiler parses all the members of the class B, and then generates the code for each method. At that point, it knows all the members and their order, and it'll construct the members in order, using the default constructors unless otherwise specified. So foo is constructed without calling explicitly its default constructor. What you need to do is just create object of Bar.

Upvotes: 1

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.

Upvotes: 17

David Thornley
David Thornley

Reputation: 57076

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.)

Upvotes: 6

Macke
Macke

Reputation: 25710

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())

Upvotes: 1

Frank Schwieterman
Frank Schwieterman

Reputation: 24478

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;
};

Upvotes: 32

Shane C. Mason
Shane C. Mason

Reputation: 7615

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)

Upvotes: 0

Naaff
Naaff

Reputation: 9333

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...
}

Upvotes: 1

JaredPar
JaredPar

Reputation: 755557

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 :)

Upvotes: 3

dirkgently
dirkgently

Reputation: 111316

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;
}

Upvotes: 1

Related Questions