Topic: Order of constructor calls for global objects


Author: patel@shell.com (Amit Patel)
Date: Thu, 1 Jul 1993 22:43:35 GMT
Raw View


Author: daniels@NeoSoft.com (Brad Daniels)
Date: Fri, 2 Jul 1993 15:58:03 GMT
Raw View
In article <PATEL.93Jul1174335@aeroflot.shell.com> patel@shell.com (Amit Patel) writes:
>From the ARM, section 3.4:
>
>"The initialization of nonlocal static objects in a translation unit
> is done before the first use of any function or object defined in that
> translation unit."

EEeeeewww!  Unless I missed something (which has been known to happen), it
is literally impossible to fully comply with that statement.  In order to
initialize a nonlocal static object, you must call the object's
constructor.  If the constructor is defined within the same translation unit,
you can never initialize it before calling its constructor, since calling
its constructor is how you initialize it!

Ignoring that problem for the moment, the only way to implement this
functionality would be for the compiler to check for initialization at
the beginning of each non-virtual function in the compilation unit.  (You
can play games with virtual member function displays to avoid later checks
on virtual member functions.)  You'd also have to add checks in every function
which uses a public data member or inline function defined on a nonlocal static
object.  Does any implementation actually comply? In my experience (which is
admittedly limited to 2 compilers), I seem to have to fiddle with link order
to get things to work.

- Brad
--
Brad Daniels   |  "Let others praise ancient times.
daniels@neosoft.com  |   I am glad I was born in these."
I don't work for NeoSoft, and | - Ovid (43 B.C. - 17 A.D)
don't speak for my employer. |




Author: ampe@cbnews.cb.att.com
Date: 2 Jul 93 21:45:00 GMT
Raw View
In article <PATEL.93Jul1174335@aeroflot.shell.com> patel@shell.com (Amit Patel) writes:
>
>From the ARM, section 3.4:
>
>"The initialization of nonlocal static objects in a translation unit
> is done before the first use of any function or object defined in that
> translation unit."

That's not quite what my ARM says:

  The initialization of nonlocal static objects in a translation unit is
  done before the first use of any function or object defined in that
  translation unit.  Such initializations may be done before the first
  statement of main() or deferred to any point in time before the first
  use of a function or object defined in that translation unit.

It's extremely unclear what is meant by the portion of this sentence
beginning with "deferred."  However, the accompanying annotation makes
the intent clear:

  ...placing the definitions of the global objects involved in a single
  translation unit is the only portable way of assuring a specific
  order.

To me, this says that global objects are an essentially useless
feature of the language since they may only be used in the trivial
case where they can all be placed in one file.  In this case, you
might as well just place all the objects in main().  Because of this,
I suggest that global objects be eliminated from the language
altogether.

Later in this same annotation E&S offer a "solution" to the dynamic
initialization problem: for every global object created in a library,
have a header file that guarantees initialization when included in a
file using the object.  There are two very major problems with this:

1) The object is initialized before construction (or constructed
twice, depending on method).  This can only work if you have a
constructor which either does nothing, or checks some static variable
to see whether it has been constructed already.  Unfortunately, this
also applies to all member objects, base classes, and members of base
classes.  If you can get this to work at all, it is almost guaranteed
to break at some point in the future, as the implementations of your
class, and all its member and base classes, evolve.

2) It doesn't even work.  Consider the following:

------- a.h ---------
struct A {
        A();
};
------- a.c ----------
#include <iostream.h>
#include "a.h"

A::A() { cout << "hello, world!\n"; }
------- main.c ---------
#include "a.h"

A a;

main(){}
-------------------

There is no guarantee that "cout" is initialized before use.  Clearly,
each class header must now be required to include all headers for any
global objects used anywhere in its implementation, and these
inclusions must be kept current as the implementation evolves -- a
recipe for disaster.

Instead of hiding these little tidbits in the depths of the ARM (and
concealing their very real dangers!), I suggest that C++ should
advertise loud and clear that global objects should be used only with
the most extreme caution, if it all.  Better yet, why not just remove
them from the language?  (What a joy to eliminate patch/munch!)  Our
project originally made extensive use of global objects, since they
seemed on the surface to be one of the most attractive features of
C++.  We have since lost many person-months of effort in removing
these (and delivered products late to customers), once we realized
that they broke when porting to new platforms.

A large side-benefit of removing global objects is that it would allow
C++ libraries to be made sharable.  This benefit should not be
underestimated for large projects; now that our libraries are
sharable, our executables are about 90% smaller, saving us greatly in
disk space, installation time, and execution time (due to reduced
paging).

John_Ampe@att.com




Author: pete@borland.com (Pete Becker)
Date: Fri, 2 Jul 1993 22:45:47 GMT
Raw View
In article <C9K4F5.9Lo@cbnews.cb.att.com> ampe@cbnews.cb.att.com writes:
>
>  The initialization of nonlocal static objects in a translation unit is
>  done before the first use of any function or object defined in that
>  translation unit.  Such initializations may be done before the first
>  statement of main() or deferred to any point in time before the first
>  use of a function or object defined in that translation unit.
>

 Once you're in code called from main() you can rely on global objects
having been constructed before any of your code uses them. The initialization
order problem arises because of interactions among the constructors for global
objects, which all occurs before entry into main().

>It's extremely unclear what is meant by the portion of this sentence
>beginning with "deferred."  However, the accompanying annotation makes
>the intent clear:
>
>  ...placing the definitions of the global objects involved in a single
>  translation unit is the only portable way of assuring a specific
>  order.
>
>To me, this says that global objects are an essentially useless
>feature of the language since they may only be used in the trivial
>case where they can all be placed in one file.

 To me it says that _if_ the constructors for global objects interact
then the objects themselves should be defined in the same translation unit so
that I can control the order of their construction. The problem is not with
global objects themselves, but with their interconnections. Avoid the
interconnections and you avoid the problem.
 I think we've all been done a disservice by the introductory books on
C++ that include problems for the reader to solve that contain things like
"write a program that displays "Hello, world" but whose main() function
only a 'return 0;'". It's helpful to remind people about side effects, but
the style of programming that this sort of example suggests puts way too much
of the program into global constructors and destructors.

 -- Pete