Question

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

Was it helpful?

Solution

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.]

OTHER TIPS

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.

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.

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");

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.

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.

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top