Reputation: 149
Is the following example legal and safe C++, or will it have a possibility of blowing up depending on what order the linker decides to invoke the global objects' constructors?
a.hpp:
class A {
public:
A(int val_);
int val;
};
extern A a;
a.cpp:
#include "a.hpp"
A::A(int val_) : val(val_) {}
A a(1234);
b.cpp:
#include <cassert>
#include "a.hpp"
class B {
public:
B(A &a);
int &ref;
};
B::B(A &a) : ref(a.val) {}
B b(a);
int main(int argc, char **argv) {
assert(b.ref == 1234);
assert(&b.ref == &a.val);
}
I need to do something like this in some real code I'm writing (obviously my A and B classes are much more complex than this minimal example, but the data members they need to share are plain old ints and bools) and I'd much rather use references than pointers.
Upvotes: 3
Views: 82
Reputation: 15207
Your question is legit but I think the module where a
is built will be correctly static initialized before the instance of b
will access it and this should be especially true if you build a
in a different shared object and the class B
has an explicit, non-circular dependency on the class A
, as it is in your example. This can be also easily tested on deployment compilers/platforms.
If you are still unconvinced, you should be able to force the inizialization of a
before b
with a free function a()
as taught here (Construct On First Use idiom):
Header:
A& a();
Compilation unit:
A& a()
{
static A _a;
return _a;
}
Then you refer to _a
with a()
.
Upvotes: 0
Reputation: 137315
Yes, this can blow up (according to the standard, anyway), because b
's constructor can run before a
's, and then ([class.cdtor]/p1)...
For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior.
Upvotes: 2