4

Before switching to C++, we found the initialization language element in Delphi extremely useful. It allowed you to have code in each unit which would be called when the program was started, so you could initialize various elements of that unit.

This does in our opinion make things easier and helps to keep the code clean.

  • So why is there no initialization and finalization in C++?

  • What are our options for replacements of this language feature in C++?

jruizaranguren
  • 12,679
  • 7
  • 55
  • 73
  • 2
    Nobody thought about it when C++ was designed! I always liked the Java static code block (which seems to have the same affect as initialize though they did not have the equivalent of static finalization). – Martin York Dec 09 '10 at 21:02
  • 9
    This is by no means the worst of the nuisances you'll find in C++. You really ought to switch back to Delphi. – Mason Wheeler Dec 09 '10 at 21:29

8 Answers8

7

The equivalent C++ feature is constructors and destructors for file-scope/global objects. For instance:

#include <iostream>
using std::cout;

struct X {
   X() { cout << "X::X()\n"; }
   ~X() { cout << "X::~X()\n"; }
};

static X x;

int main() { cout << "main()\n"; return 0; }

will output

X::X()
main()
X::~X()

when run.

It is generally considered unwise to use this feature, because you have no control whatsoever over the order in which these constructors and destructors are executed, which means things may get initialized before their dependencies, producing hard-to-debug crashes.

zwol
  • 135,547
  • 38
  • 252
  • 361
  • 2
    You have absolute control over the order (if you put them all in the same translation unit). – Martin York Dec 09 '10 at 21:07
  • 1
    The only thing you can't control is the order in which translation units get processed, but within a translation, they're processed in order of declaration, IIRC. So as long as your static doesn't depend on anything in another translation unit, you're fine. – Rob K Dec 09 '10 at 21:12
  • 1
    If you *can* put them all in the same translation unit, you could just as easily put them all at the top of `main()`. – zwol Dec 09 '10 at 21:35
3

In C++ constructor/destructor pairs are generally used for this sort of thing. Be careful when using static objects, however. Two things you should read if you want to do this:

  1. What's the "static initialization order fiasco"?

  2. How do I prevent the "static initialization order fiasco"?

Laurence Gonsalves
  • 137,896
  • 35
  • 246
  • 299
  • 1
    The static initialisation order fiasco aflicts Delphi too – David Heffernan Dec 09 '10 at 22:08
  • 2
    @David: What do you mean? The static initialization order fiasco is caused by there being no well-defined sequence for static initialization in C++. In Delphi it's clearly defined: units are initialized in the order they're compiled, as determined by the `uses` clauses, and finalized in the reverse order. This is why the static initialization order fiasco *does not* affect Delphi. – Mason Wheeler Dec 10 '10 at 01:30
  • 1
    @Mason I think you may have been living a sheltered life! In practice trying to work out the initialisation order from your source code requires knowledge of the undocumented algorithm that the compiler/linker uses. What's more it's not practical to work it out for any project of any significant size. In reality minor changes in the uses clauses of units can cause very unpredictable changes to initialisation order. So, I reassert that this is an issue in Delpi. – David Heffernan Dec 10 '10 at 09:18
  • @David: Please don't spread FUD. The project I work on at work is close to 4 MLOC with a few thousand units total, and I've never had any trouble resolving the initialization order on few times it's become an issue. (You don't need to work out the *entire* order, just whether unit A is initialized before or after B, and that can be made explicit easily enough.) And the compiler's "undocumented algorithm" is described in the official documentation: http://docwiki.embarcadero.com/RADStudio/en/Programs_and_Units#The_Initialization_Section – Mason Wheeler Dec 10 '10 at 13:15
  • @Mason There's no FUD here, and I do know that you are an expert so you don't need to prove it by tossing around you MLOCs! Consider 4 units, A, B, C and D. C uses A then B. D uses B then A. Does A get initialised before B, or vice versa? – David Heffernan Dec 10 '10 at 13:45
  • @David: Assuming, for the sake of example, that these are the only four units in the project, that depends on whether C or D gets used first in the DPR. Initialization order is based on the order in which the compiler sees a unit for the first time. – Mason Wheeler Dec 10 '10 at 14:01
  • @Mason when there are more and more units in your project, with more and more complex dependencies this gets harder and harder. What's more you can have units in your project that are not in your project file (e.g. RTL/VCL units). What order do they get initialised in. I dispute your assertion that this behaviour is documented. The documentation you referred to is incomplete and non-specific - I wouldn't count it as documentation. – David Heffernan Dec 10 '10 at 14:06
  • @David: You make it sound like it's some complicated, mystical algorithm. It's not. For each unit, starting from the DPR, the compiler checks all of its used units in the **interface**, in declared order from left to right. If it finds one it hasn't compiled yet, it recursively checks that unit in the same way, until it finds a unit with no dependencies it hasn't visited yet. It does a similar scan of the **implementation** section, in declared order, left to right, then adds that unit to the list, if it has anything to initialize. There's nothing complicated or magical going on here. – Mason Wheeler Dec 10 '10 at 14:23
  • @Mason You misunderstand. *I* know perfectly well what the algorithm is. But I've had to work it out for myself like all the rest of us. The problem is not understanding how it does it, but trying to impose some control on the order of initialisation. In normal code things get executed in the order they are written. With initialisation the control of ordering is quite remote from the code itself and applying control is thus rather opaque. However, at least control can be applied unlike with C/C++! – David Heffernan Dec 10 '10 at 14:50
3

Question 1: why isn't there a keyword?

No-one apart from Stroustrup or the committee members can really answer why C++ is how it is, but we can speculate, probably that it wasn't considered important enough for a keyword. The C++ standard does talk about order of initialization, such as for objects but the order is not strictly defined and left to the implementation. Here's one quote (3.6.2/3):

It is implementation-defined whether or not the dynamic initialization (8.5, 9.4, 12.1, 12.6.1) of an object of namespace scope is done before the first statement of main. If the initialization is deferred to some point in time after the first statement of main, it shall occur before the first use of any function or object defined in the same translation unit as the object to be initialized

Question 2: how to achieve the equivalent of the Delphi initialization and finalization keywords?

There are two options. The first has been mentioned by other posters and I don't want to copy their answers: declare an object in a certain scope (translation unit or namespace) and its constructor and destructor will be called 'sometime'; do work there.

Note that the order of this is implementation-defined, so you're already in uncertain territory.

The second option is also compiler dependent. You're using Delphi, so am I right in thinking you're using C++ Builder to compile your C++ code? If so, C++ Builder and some other compilers support the #pragma startup and #pragma exit pragmas. These pragmas call a method at a certain time when your program is starting up or shutting down.

Personally I find this a neater solution, for two reasons:

  • It specifies exactly when something will occur and I can see it written down in code

  • It allows you to call a function, instead of using a constructor or destructor. This is aesthetically cleaner and lets you write, say, initialization() and finalization() methods which perform your work. This probably gets you as close to the Delphi syntax as you can get.

You can use these pragmas to call a procedure (which takes no parameters and returns void) and also optionally specify when it should occur, using a number between 64 and 255. You need to do this only if the order of initialization or finalization matters. A higher number is called first and priorities of 0-63 are reserved. For example:

void initialization(void) { foo = 3; bar = 5; /* Do useful work here */ }
#pragma startup initialization 200

void finalization(void) { foo = 0; bar = 0; /* Do useful work here */ }
#pragma exit finalization 200

The call chain is managed by the linker and you can run into issues if you use more compiler-specific constructs, such as weak packaging, but in general this is the technique I would recommend.

Community
  • 1
  • 1
David
  • 13,360
  • 7
  • 66
  • 130
2

Class have constructors and destructors that you can use to intialize and clean up.

I think the closest you get to "code units" in C++ is classes.

Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
1

Write a class with a constructor (initialization code) and a destructor (finalization code). Declare a singleton instance of this class; the constructor will be called at startup, and the destructor before the program shuts down.

Tim Robinson
  • 53,480
  • 10
  • 121
  • 138
  • Has the same effect but is slightly hackey (if you build a static lib then this may not work without extra wiring). – Martin York Dec 09 '10 at 21:05
1

Typically, it's viewed as a code smell in C++ to have global state that needs construction or destruction, and even if you did have this, you would just declare a class that does this in it's constructor and define a file-global instance of it.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • 1
    +1: Global state is bad. But its not always bout state. Maybe it is code to decide what language dll to load (that was off the top of my head). – Martin York Dec 09 '10 at 21:07
  • 1
    @DeadMG You say this is viewed as code smell but I don't buy that. What about, say, a Windows critical section? That is typically global state which requires construction and destruction. – David Heffernan Dec 09 '10 at 21:14
  • Any program on the planet must has global info/data/states. The devil is to have too complex executable codes and the author must be carefull when these type of codes are in DLL/LIB – APZ28 Dec 09 '10 at 22:08
  • 1
    @David: Really? Because the WinAPI thread function gives you a void*. Use it. Or use a non-extremely-primitive threading library, like TBB or the VS ConcRT. @APZ28: There's no disagreement there- except that virtually all necessary global program state is handled by the compiler, like the heap and function addresses and things like that. Programmers should not need to create global state for the vast, vast, vast majority of systems. – Puppy Dec 10 '10 at 00:21
  • @DeadMG I don't think you can ever have used a critical section. The whole point of them is that they are shared. If you don't share them then they don't work!! – David Heffernan Dec 10 '10 at 09:20
  • @David: Yeah. So you put a pointer to one in your void*. Now it's shared. Voila. – Puppy Dec 10 '10 at 12:44
  • @DeadMG It's not the sharing that's the problem, it's the initialisation. At what point do you initialise the critical section? – David Heffernan Dec 10 '10 at 13:46
0

In C++ you call the constructor (equivalent to your destructor) the same name of you class and the destructor the same name of you class, prefixed with tilde (~):

Class Point {
    public:
    Point() {  }
    ~Point() { }
}
theMage
  • 159
  • 3
0

The closest feature C++ has to what you are use to is static variables (specifically, static member variables).

// A.h
class A
{
public:

private:
    static int someValue;
};

// A.cpp
int A::someValue = 2;

The static variable is initialized at program startup. There is no automatic "finalization" procedure for static members (you would have to write your own cleanup function and call it).

Zac Howland
  • 15,777
  • 1
  • 26
  • 42