我正在使用Windows XP / Visual C ++ 2008。
我遇到了一个C ++静态初始化顺序问题,我用着名的“首次使用的构造”成语解决了这个问题:
Foo foo; // Forget this
Foo &foo() // Do this instead
{
// Use ptr, not reference, to avoid destruction order problems
static Foo *ptr = new Foo();
return *ptr;
}
然而,我一直在寻找,看起来Windows(我的平台)并不保证本地静态的线程安全,尽管它确实为全局静态提供了保证。
所以,如果我使我的对象全局化,我得到线程安全但我有初始化顺序问题。如果我使用“首次使用时构造”,我会避免初始化顺序问题,但我会遇到竞争条件。如何同时解决这两个问题?
答案 0 :(得分:7)
在C ++ 2011中,您使用std::call_once()
:
#include <mutex>
void initialize(Foo*& ptr)
{
ptr = new Foo();
}
std::once_flag flag
Foo& foo()
{
static Foo* ptr(0);
std::call_once(flag, initialize, std::ref(ptr));
return *ptr;
}
如果您无法使用C ++ 2011,则可能需要查看系统的基础设施。对于POSIX,这将是pthread_once()
。如何在我不知道的其他平台上完成。
那就是说,我建议不来使用它,因为它实际上是某种形式的全局数据,并且通常没有充分的理由使用它。有例外,但很少见。实际上非常罕见。
答案 1 :(得分:0)
当你在Windows上时,你可以使用一个关键部分。这只允许一个线程在构造'lock'之后和解析之前在代码段中运行 - 这是在foo初始化之后和函数返回之前。
#include <atlbase.h>
CComAutoCriticalSection fooCs;
Foo& GetFoo()
{
CComCritSecLock<CComAutoCriticalSection> lock(cs);
static Foo foo;
return foo;
}
答案 2 :(得分:-1)
不要使用全局变量,也不要使用单例,否则就不会出现这些问题。