1

I have seen a lot of questions on this, but none include explanation on how to compile the code for this specific use case. I run the following command: g++ main.cpp c.cpp testobj.cpp -o main, but running this gives me a Segmentation fault (core dumped). When I have the print statement in the main method in main.cpp and remove all TestObj code it does work.

Is this the proper way of assigning the C::test constant?

main.cpp:

#include "c.h"
#include "testobj.h"

TestObj testobj;

int main() {
    return 0;
}

c.h:

#ifndef CONSTANTS
#define CONSTANTS

#include <string>

namespace C {
    extern std::string test;
}
#endif

c.cpp:

#include "c.h"

namespace C {
    std::string test = "test";
}

testobj.h:

#ifndef TESTOBJ
#define TESTOBJ

class TestObj {
public:
    TestObj();
};

#endif

testobj.cpp:

#include "testobj.h"
#include <iostream>
#include "c.h"

TestObj::TestObj() {
    std::cout << C::test << std::endl;
}
martijnn2008
  • 3,552
  • 5
  • 30
  • 40

2 Answers2

4

This is caused by the initialization order of global static variables. It is undefined and is known as static initialization order fiasco. When TestObj::TestObj( uses C::test - it is not yet constructed.

The common way to solve it is to move the global static variable into a function local static variable,ie:

const std::string getTestString() {
    static std::string test = "test";
    return test;
}

now, when you call getTestString() test variable will be constructed, and it will be done exactly once. Also since C++11 initialization of static variables in functions is guaranteed to be thread safe.

marcinj
  • 48,511
  • 9
  • 79
  • 100
  • Moreover, using std::cout in constructor of static variable is unsafe, because std::cout itself is static variable, and we also rely on its initialization. – Igor Semenov Apr 14 '16 at 11:36
  • @IgorSemenov I didnt think about it, since C++11 it looks like it was fixed - http://stackoverflow.com/questions/8784892/is-stdcout-guaranteed-to-be-initialized – marcinj Apr 14 '16 at 11:40
3

While the order of initialization for global variables within a single translation unit is well defined, the order between translation units is not.

So if the testobj object in the main.cpp source file is initialized before the C::test object, then you will have weird behavior indeed.

If you have multiple translation units, each with global variables, then you can't rely on the initialization order between them.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • I it save to change the declaration of `TestObj testobj` to `TestObj *testobj` and move the initialization to the `main()` method, by doing `*testobj = TestObj()`? – martijnn2008 Apr 14 '16 at 11:36
  • @martijnn2008 Yes, if you consider using pointers safe. :) The `main` function will always be called after all global variables have been initialized. – Some programmer dude Apr 14 '16 at 11:41