views:

100

answers:

3

Suppose I have a class

    class A {
      public:
      A(int i);
      ~A();
      private:
      B b;   // Want <- this guy to be constructed with i in A's constructor!
    };
I want b to be constructed in the constructor with particular parameters that aren't known until A is constructed. If I were to do the following in A's constructor:

    A::A(int i) {   
      B b(i);
      // Or even if I try to do b = B::B(i);
    }

I notice that b get's allocated twice on the stack! aghghg.

Then I found out that what I can do in A's constructor is:

A::A() : b(B::B(7)) {

}

And b only gets allocated on the stack once!

But this is pretty clunky. Anyone got a better idea? Remember, the constructor should only be called once!

Is this the standard way of allocating objects NON-dynamically with important parameters? What if we can shove b's construction into that fancy argument list thing!? You're forced to either dynamically allocate, or construct TWICE on the stack!

Bonus Question: When does b get deallocated? Is it after or right before A's destructor

+3  A: 

does

A::A() : b(7) { }

not work?

Edit: I'm at work, so I'll do a more comprehensive edit later using some profile stuff to see what gcc does w.r.t. deallocation. I suspect that nobar is right and all deallocation happens at once.

b(B::B(7)) works as well as b(7) because B::B(7) creates a temporary B variable. b is then copy-constructed from that temporary. A decent optimizing compiler should be able to reduce the second case to the first but:

  1. b(7) is more idiomatic -- other c++ programmers will recognize is more easily
  2. You really don't know for sure what a compiler will do.
  3. if B is not copy-constructible, or expensive to copy-construct, you may not want to deal with the added overhead if, like most, you turn off optimizations for debugging.
KitsuneYMG
I think so. But what's odd is that b(B::B(7)) also works!
GuestoMesto
It looks like each thing in the initialization list can either be an assignment or a constructor call! Do you know why that works?
GuestoMesto
@GuestoMensto: It works because you're creating a *temporary* `B`, then invoking the compiler-generated copy-constructor of `B`. So you're still creating two instances of `B`.
In silico
WHOAH WAIT.. B is destructed AFTER A is! This doesn't sound right at all! When you inherit a class, and provide the parent initialization it, the parent's destructor gets called BEFORE the child's! This seems to be very different.
GuestoMesto
@In silico: So does that mean that there is a way to do this without ever invoking a copy constructor? Can you be clear? Which way does NOT invoke the copy constructor: b(7) or b(B::B(7))
GuestoMesto
@GuestoMesto: Actually it makes perfect sense. The order of construction is parent then child. Destruction is the complete opposite process. Again, you need to pick up a C++ book or you will get lost.
In silico
@GuestoMesto: `b(7)` calls the constructor that can accept an integer. `B(7)` creates a temporary `B`, which you then pass to a the compiler-generated copy constructor of `B`.
In silico
Oh, cool.. Thanks! How does the compiler know that for Objects, we need to pass the stuff to the suitable constructor, but for native types, we just do assignments?
GuestoMesto
@GuestoMesto: Actually sometimes you don't need to. If `B` had a default constructor, then you wouldn't need to pass anything at all! Also, you're confusing *assignment* and *initialization*, which are actually two different things in C++. Again, I highly recommend that you pick up a [good C++ book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) so that you can properly learn these fundamental concepts.
In silico
@GuestoMesto: I think you have it right: b is a member of A, so they get *deallocated* at the same time. However, b is *destructed* AFTER instances of A are destructed (A's member data has to be valid for as long as instances of A are valid). Member b and instances of A are both *destructed* before they are both deallocated. -- The order of construction is NOT "parent then child".
nobar
@nobar: No, they do not get deallocated at the same time. In the OP's code snippet, when the `A` object is destroyed, the `A`'s destructor is called first. Then, the `b` subobject's destructor is called. The order of construction in C++ is **always** parent then child. All (non-virtual) base classes are created first, then any member subobjects, then the object itself. The order of destruction is the opposite operation, so it runs in reverse. It's not accurate to say that `b` is a child of `A`, rather, `b` is a *member* of `A`.
In silico
@In silico: I think we are talking about different things. I probably shouldn't have used the parent/child metaphor as I did. I wasn't addressing inheritance because there is no inheritance in the OP's original question. The "child" that I was referring to in this case is the "member" b, and b gets constructed before a -- the child is first.
nobar
So inheritance has: Parent.constructor -> Child.constructor -> Child.destructor -> Parent.destructor. But composition has SubElement->constructor -> Element.constructor -> Element.destructor -> SubElement->destructor. This makes perfect sense, because we can think of inheritance as just another way to "contain" data.
GuestoMesto
@In silico: I was trying to draw a distinction between *deallocation* and *destruction* because it seemed like there was some confusion about that here. I agree with what you are saying about the order in which destructors are called for A and b. However, I believe that the *deallocation* operation does not distinguish between the two -- they occupy the same memory allocation, so they are deallocated at the same time.
nobar
And in silico, Of course I have a good C++ book, and I am reading it, but sometimes discussing things with real people is actually a good way to learn in addition to reading the material, don't you agree. 99.9% of the questions on StackOverflow could be self-answered by reading a book. But It's more effective to discuss these things and leave a mark on the internet for others to search for by important keywords that we're leaving all over this post :)
GuestoMesto
@GuestoMesto: Of course, I agree with that. But however, Stack Overflow is a Q and A site for *specific* questions, and the question you asked is fairly broad. Stack Overflow is really not the best medium for discussion.
In silico
@nobar: It seems there's been a bit of a misunderstanding. When I heard "parent/child", I thought of inheritance, and I misread "deallocation."
In silico
@In silico: Yeah sorry. I think I misread a bit of the context in this comment thread which caused me to use "parent/child" erroneously thinking that it had already been used in the way I was using it.
nobar
From the answer above, "B should be deallocated before A is" is incorrect -- even if you interpret *deallocated* to actually mean *destructed*. This answer should be fixed if it is going to be marked as the best answer.
nobar
@nobar, agreed. B is destructed AFTER A, but deallocated almost simultaneously? I don't believe the order of deallocation is something that is even specified by the language.
GuestoMesto
Also, thank everyone for the discussion, but as far as what is appropriate to be asked on stackoverflow, a newbie doesn't know the obscurity/broadness of their question. Also, every single time I find exactly what I'm looking for on SO, I scroll down and there's someone saying how this type of questions shouldn't be asked on SO. Well, I found it on google and it's what I was looking for, so it looks like that *was* the type of thing that should be on SO.
GuestoMesto
Unmarked as correct answer until fixed
GuestoMesto
+7  A: 

I'm sorry to say but you have it all wrong.

What you need to do is to pick up a good beginner's C++ book. This is such a fundamental part of the language that if you don't understand this you will struggle when dealing with non-trivial C++ code.

That being said, when an object is about to be created, all subobjects will be created first. If you need to pass parameters to those subobject constructors, you need to create what's called an initializer list:

A::A(int i) : b(i) {}

The stuff that follows the colon and before the first brace is the initializer list. Only constructors can have them. What's going on here is that we pass the value of i to the b subobject's constructor. This happens before the constructor for A is called!

So for your case, the order of construction is:

  1. The b subobject
  2. The A object itself

The order of destruction is the complete opposite process.

In silico
Yah, I actually think that's exactly what I was describing. But thank you for confirming :) Edit: i see you don't need to provide the namespacing thing cool!
GuestoMesto
@GuestoMesto, The "namespace thing" that you were doing was actually creating a temporary `B` initialized with 7 which was then being assigned to the `b` member variable using `B`'s copy constructor.
joshperry
A: 

It only takes a small modification to your program to demonstrate the order of construction and destruction.

   #include <iostream>

   using std::cerr;

class B
   {
public:
   B( int ) { cerr<<"B()\n"; ;}
   ~B() { cerr<<"~B()\n"; }
   };

class A
   {   
   B b;
public:
   A( int i ) : b(i) { cerr<<"A()\n"; }
   ~A() { cerr<<"~A()\n"; }
   };

int main()
   {
   A a(7);
   }

Here's the output:

$ make destructor_order && ./destructor_order
B()
A()
~A()
~B()
nobar