我在尝试创建一个单身时想出了这个。
示例:(我正在尝试使MySelf
成为线程安全且不使用双重检查锁定的单例)
class MySelf
{
private:
string Name;
int Age;
MySelf()
{
Name = "Deamonpog";
Age = 24;
cout << "Constructing MySelf : " << Name << endl;
};
friend class MySingleton;
public:
~MySelf(){ cout << "Destructing MySelf : " << Name << endl; };
int MyAge() const
{
return Age;
}
};
class MySingleton
{
private:
static MySelf mself;
public:
static MySelf * GetInstance()
{
return &mself;
}
};
MySelf MySingleton::mself;
现在我可以轻松地使用它,
cout << "I am " << MySingleton::GetInstance()->MyAge() << endl;
我不想要延迟初始化,因为我要创建的类将从头到尾。但这个线程安全吗? (据我所知,似乎没问题)
如果可以,那么我应该使用像这样的通用编程,
template <class T>
class GenericSingleton
{
private:
static T _instance;
public:
static T * GetInstance()
{
return &_instance;
}
};
template <class T>
T GenericSingleton<T>::_instance;
所以我也可以和其他任何课程一起使用。我只需要将friend class GenericSingleton<MySelf>;
添加到所需的sigleton(例如,添加到MySelf类)。
这种实施会造成麻烦吗?我实际上正在创建一个库。有些单身人士要出口,有些则不出口。如果这不是一个库而只是另一个应用程序呢?
- 编辑 -
所以现在我必须这样做(因为我使用的VC ++仍然不支持C ++ 11),
static MySelf & GetInstance()
{
WaitForMutex(mymutex); // some function from the threading library
if( NULL == _instance )
{
_instance = new MySelf();
}
ReleaseMutex(mymutex); // release function of the same library
Return _instance;
}
告诉用户使用该功能一次,然后将其缓存以供使用。
(或者我也可以将函数重命名为Initialize()
并创建另一种方法,只需返回引用而无需任何锁定或创建。)
那么mymutex
应该在哪里?它应该在哪里初始化?
答案 0 :(得分:6)
否,但这不是主要问题。
全局对象(例如static
)的初始化在翻译单元中是无序的;意思是如果在创建一个全局变量期间我正在调用MySingleton::GetInstance()
,我最终会得到一个指向单位化内存的指针。
请参阅Initialization Order Fiasco。
最重要的是,如果我在初始化阶段期间启动了第二个线程,它可以访问部分初始化的对象。
一般来说,建议使用Meyer的Singleton:
MySelf& MySelf::Instance() { static MySelf S; return S; }
以两种方式回避初始化顺序fiasco:
Instance()
返回此外,从C ++ 11开始,这需要是线程安全的:也就是说,在构造对象时,另一个线程应该调用Instance()
它将耐心等待直到构造结束并且然后返回与所有其他线程相同的实例(gcc已在C ++ 03中完成)。
注意:使用无关的类只是更多的输入没有附加值,抛弃它。
答案 1 :(得分:5)
这是更简单的实现:
template <class T>
class GenericSingleton
{
public:
static T& GetInstance()
{
static T _instance;
return _instance;
}
};
它在 C ++ 11 中也是线程安全的,因为静态变量在任何线程之前创建。