|
||||
|
Section 10:
|
[10.16] Why doesn't the construct-on-first-use idiom use a static object instead of a static pointer?
Short answer: it's possible to use a static object rather than a static pointer, but doing so opens up another (equally subtle, equally nasty) problem. Long answer: sometimes people worry about the fact that the previous solution "leaks." In many cases, this is not a problem, but it is a problem in some cases. Note: even though the object pointed to by ans in the previous FAQ is never deleted, the memory doesn't actually "leak" when the program exits since the operating system automatically reclaims all the memory in a program's heap when that program exits. In other words, the only time you'd need to worry about this is when the destructor for the Fred object performs some important action (such as writing something to a file) that must occur sometime while the program is exiting. In those cases where the construct-on-first-use object (the Fred, in this case) needs to eventually get destructed, you might consider changing function x() as follows:
// File x.cpp
#include "Fred.h"
Fred& x()
{
static Fred ans; // was static Fred* ans = new Fred();
return ans; // was return *ans;
}
However there is (or rather, may be) a rather subtle problem with this change.
To understand this potential problem, let's remember why we're doing all this
in the first place: we need to make 100% sure our static object (a) gets
constructed prior to its first use and (b) doesn't get destructed until after
its last use. Obviously it would be a disaster if any static object got used
either before construction or after destruction. The message here is that you
need to worry about two situations (static initialization and static
deinitialization), not just one.
By changing the declaration from static Fred* ans = new Fred(); to static Fred ans;, we still correctly handle the initialization situation but we no longer handle the deinitialization situation. For example, if there are 3 static objects, say a, b and c, that use ans during their destructors, the only way to avoid a static deinitialization disaster is if ans is destructed after all three. The point is simple: if there are any other static objects whose destructors might use ans after ans is destructed, bang, you're dead. If the constructors of a, b and c use ans, you should normally be okay since the runtime system will, during static deinitialization, destruct ans after the last of those three objects is destructed. However if a and/or b and/or c fail to use ans in their constructors and/or if any code anywhere gets the address of ans and hands it to some other static object, all bets are off and you have to be very, very careful. There is a third approach that handles both the static initialization and static deinitialization situations, but it has other non-trivial costs. I'm too lazy (and busy!) to write any more FAQs today so if you're interested in that third approach, you'll have to buy a book that describes that third approach in detail. The C++ FAQs book is one of those books, and it also gives the cost/benefit analysis to decide if/when that third approach should be used. |
|||