GuestoMesto
GuestoMesto

Reputation: 21

Creating c++ object on the stack, trying hard not to ever allocate

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

Upvotes: 2

Views: 204

Answers (3)

KitsuneYMG
KitsuneYMG

Reputation: 12901

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.

Upvotes: 2

Brent Bradburn
Brent Bradburn

Reputation: 54859

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

Upvotes: 0

In silico
In silico

Reputation: 52149

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.

Upvotes: 7

Related Questions