views:

72

answers:

3

Say I have defined a variable like this (C++):

static const char str[] = "Here is some string data";

And I have a statically allocated class instance which references this array in its destructor, can this go wrong? E.g. could the str variable somehow get invalid?

class A {
 ~A() {
   cout << str << endl;
 }
};

static A a;

My assumption is that it can't go wrong, but I can find it clearly stated anywhere. I want to know this for sure. My assumption is that we can not predict the sequence in which destructors for statically allocated objects are called but that the data itself is never really freed until the process is torn down. Meaning pointers to POD should be safe, but not object instances.

Meaning e.g. this:

static const QString str = "Here is some string data";

or

static const std::string str = "Here is some string data";

Can not safely be used in A's destructor because they both allocate their string data on the heap and this might be freed by the destructor before A's destructor is called.

Is my assumption right and are there any sections in the C++ standard explaining this or some link to some other authority who can verify this?

+1  A: 

The order in which destructors for automatic, global, and static objects are called is always well defined: it's the reverse of the order in which the constructors were called. So if object A references an object B and B was constructed before A, you can be sure that B is destructed after A. That leaves us with the question of the order of constructors.

Here's what I remember:

  1. Global and static objects are constructed before main() is called.
  2. Class-local statics are constructed before any object of their class.
  3. Function-local statics are constructed when their scope is reached for the first time.
  4. Global and static objects within the same translation unit are constructed in the order they are defined. (That means that the order of inclusion of headers might affect this!)
  5. The order of construction of global and static objects across translation units is undefined.

I'm a bit hazy on some of these, so please correct me if you think this isn't right.

sbi
Statics in a give TU are initialized before the first function from the TU is called. Class statics are not initialized before objects of their class, but depending on where their initialization is placed (i.e. which TU it is in, and which other objects of static storage duration it is before or after). The problematic case comes when a static from TU A calls a function from TU B which references a static from TU A that hasn't yet been initialized.
Anthony Williams
@Anthony: I felt I was not sure here. IIUC, you're saying that my rule #2 is wrong and #4 needs an addition (statics of a TU are initialized before any function of that TU is called) Did I get this right? Also, what counts as a function for this? I suppose not the ctors for these statics? And what about functions called from those ctors?
sbi
Your rule #2 is wrong. Your rule #4 is right. Rule #5 needs an addition: if objects from a TU have not been initialized when `main` is called then they must be initialized before any object defined in that TU is used or any function defined in that TU is called. However, for initializations before `main` is called then you cannot rely on things from other TUs being initialized.
Anthony Williams
@@Anthony: Now I am thoroughly confused. Why don't you just change the answer yourself? (I made it CW, so that I won't earn rep on your knowledge. `:)`)
sbi
I assume that by construction and destruction you mean call to constructor and destructor. But what I am really interested in is the allocation and deallocation steps which have been merged with initialization and deinitialization in C++. We know that allocation happens before the constructor is called and deallocation happens after destructor is run. However is it defined whether deallocation ever happens for static data, even after destructor is run?
Adam Smith
@Adam: When you define "deallocation" as "giving up on the memory used for something" then every piece of memory will, eventually, be deallocated, at latest when the process exits.
sbi
A: 

If I remember correctly, global object initialization is not defined in the standard (or poorly defined) making global objects hard to reference each other.

If you want to be sure about initialization order, use a global function with your static object in it, that just return it. You're now garanteed that the static object will be initialized on first function call.

Destruction will occur at the end of the application, once out of main().

Klaim
My main concern here is really the destruction phase. Does statically allocated data ever get deallocated? (I know that destructors will run, but that doesn't mean the data necessarily gets deallocated).
Adam Smith
They are deallocated but after main() and in an undefined order. The only other way to be sure of the order is to make a singleton with creation and destruction methods.
Klaim
A: 

Okay I tried to read the C++ standard myself to find some answers. I see from the answers I get that there is a lot of confusion about difference between constructing an object and allocating it.

From the standard:

3.6.2 Initialization of non-local objects

Objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization takes place. A reference with static storage duration and an object of POD type with static storage duration can be initialized with a constant expression (5.19); this is called constant initialization. Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. Static initialization shall be performed before any dynamic initialization takes place. Dynamic initialization of an object is either ordered or unordered.

My interpretatio of this is that a const char[] will always be guaranteed to have been set before any constructor is run.

3.6.3 Termination Destructors (12.4) for initialized objects of static storage duration (declared at block scope or at namespace scope) are called as a result of returning from main and as a result of calling std::exit (18.3). These objects are destroyed in the reverse order of the completion of their constructor or of the completion of their dynamic initialization. If an object is initialized statically, the object is destroyed in the same order as if the object was dynamically initialized.

From what I can read from this POD types with constant expression will be initialized before any object types and destroyed after any object types. Meaning no code will run that can access them while they are not valid.

Which should explain why Google's C++ code standard says you should only use POD types with constant expressions.:

As a result we only allow static variables to contain POD data. This rule completely disallows vector (use C arrays instead), or string (use const char []).

Adam Smith