|
|
| Zeile 1: |
Zeile 1: |
| − | {{BenutzerSeitenNichtVeraendernWarnung|rdiez}}
| + | #REDIRECT [[Benutzer:Rdiez/PageRemoved]] |
| − | | + | |
| − | = How to write maintainable initialisation and termination code =
| + | |
| − | | + | |
| − | The traditional, C-style way is still the best one:
| + | |
| − | | + | |
| − | int main ( int argc, char * argv[] )
| + | |
| − | {
| + | |
| − | InitA();
| + | |
| − | InitB();
| + | |
| − | InitC();
| + | |
| − | ...
| + | |
| − | create some threads
| + | |
| − | ...
| + | |
| − | do some work
| + | |
| − | ...
| + | |
| − | wait for thread termination
| + | |
| − | ...
| + | |
| − | FiniC();
| + | |
| − | FiniB();
| + | |
| − | FiniA();
| + | |
| − |
| + | |
| − | // Here you can check for memory and file descriptor leaks.
| + | |
| − |
| + | |
| − | return whatever;
| + | |
| − | }
| + | |
| − | | + | |
| − | Don't over-engineer it! The method above is not cool, but is perfectly adequate. It's
| + | |
| − | easy to understand and to maintain. Sometimes it can be tricky to find the right
| + | |
| − | initialisation order, but then it will be explicitly described, and above all,
| + | |
| − | it will remain the same no matter what the other code does.
| + | |
| − | | + | |
| − | The only real drawback is that, if you have different applications/programs,
| + | |
| − | you need to write a new version of the above for each one,
| + | |
| − | and probably most of it will be copy-paste duplication. But that is a minor inconvenience.
| + | |
| − | | + | |
| − | == Do not define global or static object instances, use pointers instead ==
| + | |
| − | | + | |
| − | Global or static object instances generate constructor calls that are executed before main(),
| + | |
| − | and that may be too early, if not today, then probably in the future.
| + | |
| − | | + | |
| − | Besides, singleton objects tend to use other global, singleton objects, and the initialisation order
| + | |
| − | of such objects is not guaranteed in the C standard. The order in which the constructors are called may depend
| + | |
| − | on your operating system, on your compiler version, or even on the order in which the object modules
| + | |
| − | are listed on your makefile (the order in the linker command line).
| + | |
| − | | + | |
| − | If you want to reuse the code in an embedded application, you may find it difficult to control
| + | |
| − | the initialisation order, especially since it is often poorly documented or hard to adapt.
| + | |
| − | For example, think about the PowerPC EABI, which injects a hidden __eabi() call in
| + | |
| − | main() in order to initialise C++ exception support and so on. Say you want to use your own
| + | |
| − | memory allocator and __eabi() is calling some C++ constructors before your code runs:
| + | |
| − | you'll have to learn much more about EABI and about the C runtime initialisation than you ever wanted to.
| + | |
| − | | + | |
| − | If you need to move the code to a Windows DLL or to a Unix Shared Object at a later point in time, you will have to rewrite the initialisation logic anyway.
| + | |
| − | | + | |
| − | Therefore, always use global or static pointers, and let them be NULL on start-up, then follow the initialisation method described above:
| + | |
| − | | + | |
| − | CreateSingletonA();
| + | |
| − | CreateSingletonB();
| + | |
| − | ...
| + | |
| − | GetSingletonA()->UseIt(); GetSingletonB()->UseIt(); ...
| + | |
| − | GetSingletonA()->UseIt(); GetSingletonB()->UseIt(); ...
| + | |
| − | ...
| + | |
| − | DestroySingletonB();
| + | |
| − | DestroySingletonA();
| + | |
| − | | + | |
| − | == Do not initialise global singletons on first touch ==
| + | |
| − | | + | |
| − | Unless you really need the performance improvement that lazy initialisation may provide, avoid
| + | |
| − | using such an object creation pattern:
| + | |
| − | | + | |
| − | MyClass * GetSingleton ( void )
| + | |
| − | {
| + | |
| − | static MyClass obj;
| + | |
| − | return &obj;
| + | |
| − | }
| + | |
| − | | + | |
| − | Reasons to avoid it are:
| + | |
| − | | + | |
| − | * The function above may not be thread-safe on all platforms, especially on embedded targets.
| + | |
| − | * The destructor will run after main(), making memory leak detection difficult. <br/> An error during destruction may need to be logged, but the logger object may have been destroyed already.
| + | |
| − | * The destruction order for such objects is not specified, not portable, etc. <br/> At some point in the future, you may need to destroy some other singleton beforehand, and that's impossible to do in a portable way.
| + | |
| − | * The initialisation order will depend on the code that uses the singletons. <br/> If the usage pattern changes, the object may initialise too early or too late. It is not immediately obvious that moving a call to GetSingleton() may change the initialisation or destruction order.
| + | |
| − | | + | |
| − | == Forget about atexit() and friends ==
| + | |
| − | | + | |
| − | You'll have a hard time trying to control the destruction order in a portable manner. Some embedded platforms do not have atexit() support.
| + | |