Topic: Reconstructing an object


Author: sj@aracnet.com (Scott Johnson)
Date: 1996/12/09
Raw View

I have code similar to the following:


class Foo {
    // data members
    public:
    Foo (Void) {} // default constructor does nothing
 Foo (int arg);  // this constructor does something useful

 // rest of methods
};

Foo foo; // Static instance of a Foo, default constructor (which
  // does nothing) will be called

int main (int argc, char **argv)

{
 int x;

 // Do something useful--including assignment to x

 // OK, now is the time where I REALLY want to construct object foo

 (void) new (&foo) Foo (x); // Use placement new to
     // reconstruct the object with the
     // parameter I really want.

 // rest of code


 return 0;
}


In other words, I want a static object but wish to postpone construction
of the object until sometime after main() starts (probably to avoid order
dependencies inherent in construction of static objects).  To do this, I
have an innocous default constructor (or, if the default constructor does
do something, I can declare an enum type called do_nothing and have a
constructor which takes it as a parameter) and then use placement new
to re-construct the object when I'm actually ready.

The obvious question is:  Might I run unto trouble by constructing an
object twice in this manner?  I will not touch the object until after the
second constructor completes.  I'd rather not use an init function
(because I may have const members to initialize).  I suspect that this
approach will work, but I also suspect that it is undefined behavior
(though a cursory earch of the DWP reveals no language to this effect.)

One other approach, suggested by the humble keeper of the C++ FAQ, Dr.
Cline, is to make the object a static function object--this will cause it
to be constructed the first time the function is called.

Comments?

Scott

--
/--------------------------------------------------------------------------\
|Scott Johnson -- Professional (sometimes) SW Engineer and all-purpose Geek|
|I don't speak for nobody but myself, which everyone else is thankful for  |
\--------------------------------------------------------------------------/


[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: nickt@mailhost.bain.com.au (Nick Thurn)
Date: 1996/12/10
Raw View
Scott Johnson (sj@aracnet.com) wrote:
>
> I have code similar to the following:
>
> class Foo {
>     // data members
>     public:
>     Foo (Void) {} // default constructor does nothing
>  Foo (int arg);  // this constructor does something useful
>
>  // rest of methods
> };
>
> Foo foo; // Static instance of a Foo, default constructor (which
>   // does nothing) will be called
>
> int main (int argc, char **argv)
>
> {
>  int x;
>  // Do something useful--including assignment to x
>  // OK, now is the time where I REALLY want to construct object foo
>
>  (void) new (&foo) Foo (x); // Use placement new to
>      // reconstruct the object with the
>      // parameter I really want.
>  // rest of code
>  return 0;
> }
>
> In other words, I want a static object but wish to postpone construction
> of the object until sometime after main() starts (probably to avoid order
> dependencies inherent in construction of static objects).  To do this, I
> have an innocous default constructor (or, if the default constructor does
> do something, I can declare an enum type called do_nothing and have a
> constructor which takes it as a parameter) and then use placement new
> to re-construct the object when I'm actually ready.
>
Hi Scott,

An idiom used by the DBTools library is one solution. There are two
classes, interface and implementation. The interface class is just a
pointer to the implementation and a set of forwarding functions. The
default ctor simply sets the pointer to either null or a default
implementation object. The implementation is reference counted by
the interface. (There is also the possibility to provide different
implementations via different ctor's which can be nice.)

Using this idiom your code becomes:

// Foo.h

class Foo
{
   class Imp; // Foo::Imp declared and implemented privately
     // in foo.cpp say.
  public:
 Foo();     // imp set to null
 Foo(const Foo& f); // copies f's implementation and inc's
 Foo(int);    // a Foo::Imp created here and inc'ed

 ~Foo(); // dec's imp, delete on zero
 Foo& operator=(const Foo& f); // copies f's imp and inc's
      // dec's oldimp, delete on zero
 // blah blah...
  private:
 Imp* imp;
};

// main.cpp

#include "foo.h"

Foo foo;

int main(int argc, char *argv[])
{
 int x;
 // blah as before...

 Foo realFoo(x);

 foo = realFoo; // static foo now shares realFoo's implementation
         // in this case foo = x; would also have worked
 //...
 return 0;
}

If you want to beat the initialisation order problem, a technique I have
used for a while is pretty easy. In my case I wanted the static instances
to know each other or at least be accessible. To do this each instance
contains a "next" pointer and a static pointer is declared either as
a member or hidden in the .cpp file.

class Foo {
  public:
 Foo();
 //...
  private:
 Foo* next;
 static Foo* chain;
};
// foo.cpp
Foo* Foo::chain;

Foo::Foo() {
 next = chain;
 chain = this;
}

To initialize all Foos call some static Foo::method that traverses the
chain. This technique has the disadvantage of not allowing heap
creation of Foo's and as there is no way to prevent stack instances
other than documentation it can be dangerous. Also dynamic librarys
cause problems. Despit all this I have found it useful.

Hope this helps

cheers
Nick (my opinions only)

PS: Take a look at John Lakos' excellent book "Large Scale C++ Software
Design", a must have.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1996/12/10
Raw View
sj@aracnet.com (Scott Johnson) writes:

>Foo foo; // Static instance of a Foo, default constructor (which
>  // does nothing) will be called
>
>int main (int argc, char **argv) {
> int x;
>
> // Do something useful--including assignment to x
>
> // OK, now is the time where I REALLY want to construct object foo
>
> (void) new (&foo) Foo (x); // Use placement new to
>     // reconstruct the object with the
>     // parameter I really want.

That is strictly conforming, so long as Foo is not const and does not
have any const sub-objects.

>The obvious question is:  Might I run unto trouble by constructing an
>object twice in this manner?  I will not touch the object until after the
>second constructor completes.  I'd rather not use an init function
>(because I may have const members to initialize).

Unfortunately, this trick will only work in the same cases that an init
function would work, namely when there are no const members to initialize.
If there are const members, then it is undefined behaviour by
3.8[basic.life]/9:

]   3.8  Object Lifetime                                      [basic.life]
]
] 9 Creating a new object at the storage location that a const object with
]   static or automatic storage duration occupies or, at the storage loca-
]   tion that such a const object used to occupy before its lifetime ended
]   results in undefined behavior.  [Example:
]           struct B {
]                   B();
]                   ~B();
]           };
]           const B b;
]           void h() {
]                   b.~B();
]                   new (&b) const B; // undefined behavior
]           }
]    --end example]

The example here refers only to the top-level object being const, but
the rule itself applies equally well to const objects that happen to be
sub-objects of non-const complete objects.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]