I just read, that this construct:
const bg::AppSettings& bg::AppSettings::GetInstance()
{
static AppSettings instance;
return instance;
}
is a thread safe and working way to create a singleton?! Am I correct, that the static AppSettings variable will be the same, every time I call this method?! I get a little bit confused about the scoping on this one ...
My normal approach was to use a unique_ptr as a static member of my class ... but this seems to work...can someone explain to me, what's going on here?!
And btw.: does the const make sense here?!
In C++11 (and forward), the construction of the function local static AppSettings
is guaranteed to be thread-safe. Note: Visual Studio did not implement this aspect of C++11 until VS-2015.
The compiler will lay down a hidden flag along side of AppSettings
that indicates whether it is:
The first thread through will find the flag set to "not constructed" and attempt to construct the object. Upon successful construction the flag will be set to "is constructed". If another thread comes along and finds the flag set to "being constructed", it will wait until the flag is set to "is constructed".
If the construction fails with an exception, the flag will be set to "not constructed", and construction will be retried on the next pass through (either on the same thread or a different thread).
The object instance
will remain constructed for the remainder of your program, until main()
returns, at which time instance
will be destructed.
Every time any thread of execution passes through AppSettings::GetInstance()
, it will reference the exact same object.
In C++98/03, the construction was not guaranteed to be thread safe.
If the constructor of AppSettings
recursively enters AppSettings::GetInstance()
, the behavior is undefined.
If the compiler can see how to construct instance
"at compile time", it is allowed to.
If AppSettings
has a constexpr
constructor (the one used to construct instance
), and the instance
is qualified with constexpr
, the compiler is required to construct instance
at compile time. If instance
is constructed at compile time, the "not-constructed/constructed" flag will be optimized away.
The behavior of your code is similar to this:
namespace {
std::atomic_flag initialized = ATOMIC_FLAG_INIT;
std::experimental::optional<bg::AppSettings> optional_instance;
}
const bg::AppSettings& bg::AppSettings::GetInstance()
{
if (!initialized.test_and_set()) {
optional_instance.emplace();
}
return *optional_instance;
}
By having a thread-safe flag that lives for the entire duration of the program, the compiler can check this flag each time the function is called and only initialize your variable once. A real implementation can use other mechanisms to get this same effect though.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With