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?

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?

graphics/new_icon.gif

This is the most restrictive of all scenarios since it means that the construction must occur before the object is first used, and it must be destructed after its last use. The solution is called the nifty counter technique. What happens is that a static counter is created (the nifty counter) along with a static object in each source file whose constructor increments this nifty counter and whose destructor decrements the nifty counter. When the nifty counter is incremented from zero, the static object is initialized, and when the nifty counter is decremented to zero, the static object is destructed.

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 } 

(1) This declares nested class Fred::Init

(2) So Fred::Init can access Fred::wilma_

(3) This is now a static pointer

(4) The key: a static Fred::Init object defined in the header file

(5) This is now safe

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.



C++ FAQs
C Programming FAQs: Frequently Asked Questions
ISBN: 0201845199
EAN: 2147483647
Year: 2005
Pages: 566
Authors: Steve Summit

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net