views:

1769

answers:

8

Hi,

I have encountered a strange behavior with a simple C++ class.

classA.h

class A
{
public:
  A();
  ~A();
  static const std::string CONST_STR;
};

classA.cpp

#include "classA.h"
#include <cassert>

const std::string A::CONST_STR("some text");

A::A()
{
  assert(!CONST_STR.empty()); //OK
}

A::~A()
{
  assert(!CONST_STR.empty()); //fails
}

main.cpp

#include <memory>  
#include <classA.h>  

std::auto_ptr<A> g_aStuff; 

int main() 
{ 
  //do something ... 
  g_aStuff = std::auto_ptr<A>(new A()); 
  //do something ... 
  return 0; 
}

I'd expect access violations or anything similar, but I'd never expect that the content of the static const string could change. Does anyone here have a good explanation what happens in that code?

thanks, Norbert

A: 

It might happen if there is a global instance of A (or a static class member of type A). Since the order of the initialization of globals and statics is not defined (cross translation units), it can be.

Cătălin Pitiș
+2  A: 

Edit: Apparently the missing A:: was a typo in the original post of the code.

Original Answer:

Do you mean to have


    const std::string A::CONST_STR("some text");

so that the CONST_STR is part of class A?

Otherwise you are declaring it separately and not initializing the static member of A.

crashmstr
+1  A: 

The

const std::string CONST_STR("some text");
defined in classA.cpp is not a member of A. That definition would look like:

const std::string A::CONST_STR("some text");
Derrick Turk
+1 for being right, but why is the test in the constructor passing? A::CONSTR_STR should be empty at that point.
David Seiler
That's the point of the static member CONSTR_STR. It's a global and is initialized before main() is run.
Magnus Skog
Oh, ok, the lack of scope resolution was just a typo.
David Seiler
The matter is even so that it isn't required to be initialized before main is run: It can be initialized later. C++ initialization rules are a nightmare, really
Johannes Schaub - litb
+2  A: 

You're creating 2 static variables in two different compilation units. There is no way to tell in which order they are initialized. But their destructors are always called in reverse order.

In your case it seems that next scenario took place:

g_aStuff constructor 
CONST_STR constructor

main funciton initializes g_aStuff

CONST_str destructor 
g_aStuff descrutor

At this point result of CONST_STR.empty() is undefined. Which may trigger assert.

inazaruk
unfortunately not. the code does compile well, the missing A:: was just a type again on writing the example code. the example was so easy that I just wrote it in the text field here and did not compile it. sorry.
A: 

sorry for the double posting. changes added in my first post.

You should update the question instead
crashmstr
It would be better idea to modify your question.
inazaruk
you may delete this comment if you wish
inazaruk
+1  A: 

I'd expect access violations or anything similar, but I'd never expect that the content of the static const string could change.

Undefined behaviour: it is undefined. If CONST_STR has been destroyed, then you are not guaranteed a hardware exception if you access it. It might crash, but then again its address might end up containing data which looks like an empty string: its destructor might clear pointers or whatever.

In this case, you say that the A instance is also stored in a global smart pointer, which is assigned in main(). So the CONST_STR has been constructed when it is accessed in the A constructor, but quite possibly is destroyed before the smart pointer is destroyed. We'd need the whole program to say for sure.

[Edit: you've done that. Since CONST_STR and g_aStuff are defined in different compilation units, their relative order of construction is not defined by the standard. I'm guessing that CONST_STR is being destroyed first.]

Steve Jessop
A: 

Looking at your full code, you're relying on the order of destruction across compilation units (classA.cpp and main.cpp). If you create g_aStuff as a local in main, your asserts should pass.

markh44
+1  A: 

The standard doesn't specify the order of initialization of global/static objects in different translation units. It does, however, guarantee that each such object will be initialized before any function from that translation unit is executed.

In your case, it happens that CONST_STR is initialized after g_aStuff and, since the order of destruction is reverse from the order of construction, it gets destroyed before it. Thus, accessing CONST_STR from A's destructor invokes undefined behavior - you might get an access violation or you might not.

CONST_STR is, however, initialized before A's constructor is executed because they are in the same translation unit.

Bojan Resnik