我有这样的单身结构:
// Hpp
class Root : public boost::noncopyable
{
public:
~Root();
static Root &Get();
void Initialize();
void Deinitialize();
private:
Root(); // Private for singleton purposes
static Root *mInstance;
Manager1 *mManager1;
Manager2 *mManager2;
};
// Cpp
Root *Root::mInstance = nullptr;
Root::Root()
{
mInstance = this;
// Managers are using `mInstance` in their constructors
mManager1 = new Manager1();
mManager2 = new Manager2();
mInstance->Initialize();
}
Root::~Root() { delete mManager1; delete mManager2; }
Root &Root::Get()
{
if (mInstance == nullptr) mInstance = new Root();
return *mInstance;
}
void Root::Deinitialize()
{
delete mInstance;
}
以下是这个单身人士的用法:
Root::Get();
// Some code calling related to mManager1 and mManager2
Root::Get().Deinitialize();
问题是:
mInstance
(手动调用dtor)。因为用户可能忘记调用Deinitialize()
方法。答案 0 :(得分:3)
对于退出main()
时未访问单例的单线程应用程序,您可以使用一种相当简单的方法自动完成所有操作:
Root& Root::get() {
static std::unique_ptr<Root> rc(new Root());
return *rc;
}
此上下文中的static
表示在第一次调用函数时初始化变量,然后保持put。 C ++运行时安排static
变量rc
在某个时刻被销毁。对于在进入main()
之前启动线程的多线程应用程序,您需要一种不同的方法,以确保静态变量仅由线程初始化。
那就是说,请注意我强烈建议不使用反 -pattern Singleton (也称为全球数据)。上面的代码示例不构成任何形式的推荐!您想要使用单例的有效用途很少,大多数用途都不是。我见过的所有有效用途都使用不可变的Singleton。可变单例对象往往成为同步点,并倾向于像全局数据那样模糊使用数据。
答案 1 :(得分:1)
如果您不使用Visual Studio或C ++ 11,则不会有unique_ptr&lt;&gt ;.在这种情况下,你应该坚持使用boost :: scoped_ptr.hpp,因为很快就会完全弃用std :: auto_ptr。
#include <boost/scoped_ptr.hpp>
#include <iostream>
class Foo {
Foo() { std::cout << "constructor" << std::endl; }
public:
static Foo& Get() {
static boost::scoped_ptr<Foo> ptr(new Foo);
return *ptr;
}
~Foo() { std::cout << "destructor" << std::endl; }
};
int main() {
Foo& f = Foo::Get();
// f is now valid until the program exits.
//Then it is destroyed before the program finishes exiting.
std::cout << "Work here" << std::endl;
}
或者您可以编写自己的简化scoped_ptr。
template<typename _T>
class scoped_ptr {
_T* const mPtr;
public:
scoped_ptr(_T* const t) : mPtr(t) {}
~scoped_ptr() { delete mPtr; }
operator _T* () { return const_cast<_T*>(mPtr); }
// add more operators like -> if you want them
};
答案 2 :(得分:1)
你是否考虑过如果你的单身人士没有被删除会发生什么?
根据定义,单例是由许多客户端对象共享的单个对象,因为客户端往往会来去。大多数情况下,只要您的过程,您的单身人士就会活着。
当您的进程关闭时,操作系统将回收大多数资源(内存,文件句柄,套接字......),分配该单例然后让它死掉是完全正常的。这是我的建议。
将Get_stance()中的单例的Initialize()函数放入(如pdillon3演示)而不是让应用程序显式调用Initialize()也是一个好习惯。您没有指定此部件,如果您的项目是可执行文件,您应该可以使用现有设计。但是,如果将此代码放在DLL中,请小心。有些人认为DllMain是初始化单例对象的好地方,但事实并非如此。在DllMain期间,加载程序锁定全局关键部分被锁定,单例初始化往往会导致各种麻烦。
现在,而不是单个界面中的3个方法,只需要一个:GetInstance(),简单明了。
最后,正如Dietmar所说(虽然我仍然认为Singleton是一种模式,而不是反模式而GoF同意我),你应该考虑你是否真的需要一个单身人士。我经常使用它们,但只有当我别无选择时才会使用它们,而不是因为它们很方便。当替代品更加邪恶时,它们确实是最后的设计模式。不要因为方便而使用它们。