|
|
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.
| + | |