C++11 消除了对锁定的需求。如果静态局部变量已经被初始化,并发执行将等待。
§6.7 [stmt.dcl] p4
如果在初始化变量时控制同时进入声明,则并发执行将等待初始化完成。
对于 C++03,我们有这个:
§6.7 [stmt.dcl] p4
具有静态存储持续时间 (3.7.1) 的所有本地对象的零初始化 (8.5) 在任何其他初始化发生之前执行。POD 类型 (3.9) 的本地对象具有用常量表达式初始化的静态存储持续时间,在首次进入其块之前对其进行初始化。允许实现在与允许实现在命名空间范围内静态初始化具有静态存储持续时间的对象(3.6.2)相同的条件下执行具有静态存储持续时间的其他本地对象的早期初始化。否则,此类对象在控件第一次通过其声明时被初始化;
最后一部分很重要,因为它适用于您的代码。控制第一次进入get_class_instance()
时,先经过临界区的初始化,再经过单例的声明(这样会在临界区内初始化),再经过临界区的反初始化。
所以从理论上讲,你的代码应该是安全的。
现在,这可以改进,因为不进入每个函数调用的关键部分。@Chethan 的基本思想是合理的,所以我们将以此为基础。但是,我们也将避免动态分配。然而,为此,我们依赖于 Boost.Optional:
#include <boost/optional.hpp>
Class& get_class_instance() {
static boost::optional<Class> c;
static bool inited;
if (!inited){
EnterCriticalSection(&cs);
if(!c)
c = Class(data);
LeaveCriticalSection(&cs);
inited = true;
}
return *c;
}
Boost.Optional 避免了默认初始化,双重检查避免在每个函数调用时进入临界区。Class
然而,这个版本在赋值中引入了对复制构造函数的调用。解决方案是就地工厂:
#include <boost/utility/in_place_factory.hpp>
#include <boost/optional.hpp>
Class& get_class_instance() {
static boost::optional<Class> c;
static bool inited;
if (!inited){
EnterCriticalSection(&cs);
if(!c)
c = boost::in_place(data);
LeaveCriticalSection(&cs);
inited = true;
}
return *c;
}
我要感谢@R。Martinho Fernandes 和@Ben Voigt 合作完成了这个最终解决方案。如果您对该过程感兴趣,请随时查看成绩单。
现在,如果您的编译器已经支持某些 C++11 功能,但不支持静态初始化的东西,您还可以std::unique_ptr
结合使用 Placement new 和静态对齐缓冲区:
#include <memory> // std::unique_ptr
#include <type_traits> // alignment stuff
template<class T>
struct destructor{
void operator(T* p) const{
if(p) // don't destruct a null pointer
p->~T();
}
};
Class& get_class_instance() {
typedef std::aligned_storage<sizeof(Class),
std::alignment_of<Class>::value>::type storage_type;
static storage_type buf;
static std::unique_ptr<Class, destructor> p;
static bool inited;
if (!inited){
EnterCriticalSection(&cs);
if(!p)
p.reset(new (&buf[0]) Class(data));
LeaveCriticalSection(&cs);
inited = true;
}
return *p;
}