FAQ 16.17 What if the static object's destructor has important side effects that must eventually occur and the static object must be accessed by another static object's destructor?
The following code shows how to apply this technique to the example from the previous FAQ. The static data member is back, but this time it is a static pointer. Nested class Fred::Init has a static counter (the nifty counter) called count_, which is incremented by Fred::Init's constructor and decremented by Fred::Init's destructor. The Fred::wilma_ object is created when the nifty counter is incremented from zero and is destructed when the counter is decremented back to zero. Class Wilma is not shown since it is unchanged from the example in the previous FAQ. Here is the header file Fred.hpp. class Fred { public: Fred() throw(); protected: class Init; <-- 1 friend Init; <-- 2 static Wilma* wilma_; <-- 3 }; class Fred::Init { public: Init() throw() { if (count_++ == 0) wilma_ = new Wilma(); } ~Init() throw() { if (--count_ == 0) delete wilma_; } private: static unsigned count_; }; static Fred::Init fredInit; <-- 4 inline Fred::Fred() throw() { cout << "Fred ctor\n"; wilma_->f(); <-- 5 }
Here is the source file "Fred.cpp": #include "Fred.hpp" unsigned Fred::Init::count_ = 0; Wilma* Fred::wilma_ = NULL; Every source file that includes header file Fred.hpp ends up with its own static Fred::Init object called fredInit. Since this static object appears very early in the source file, it is initialized before most other static objects in the source file (in particular, it is guaranteed to be initialized before any static object in the source file could call Fred::Fred(), since the call to any member function of class Fred can occur only after the header of class Fred has been #included). Of all the source files that include header file Fred.hpp, one of them, say foo.cpp, is initialized first. During the static initialization of foo.cpp, the nifty counter Fred::Init::count_ is incremented from zero, and the Fred::wilma_ object is created. Since the Fred::wilma_ object is initialized before any calls to any member functions of class Fred can be made, it is guaranteed to be constructed before it is used. The static deinitialization situation is similar but opposite. Of all the source files that include Fred.hpp, one of them, say foo.cpp, is the last one to be deinitialized. Since deinitialization occurs in bottom to top order, the static Fred::Init object in file foo.cpp is one of the last things that is destructed (certainly it is destructed after any static object could call any member function of class Fred). Therefore the Fred::wilma_ object is destructed just after the last static object could possibly use it: it will not be used after it has been destructed. Unfortunately the nifty counter technique also has problems. Although it never allows an object to be used either before construction or after destruction, it does force a small amount of static initialization code into every source file that includes header file Fred.hpp. This means that a large percentage of the application needs to be paged into memory during startup, which can significantly degrade startup performance, especially if there are a lot of source files that include headers that use the nifty counter technique. |