2012-01-24 150 views
1

我有这样的单结构:Singleton实例释放

// 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

如果用户不调用'Deinitialize' - 你怎么知道它应该被调用?你会怎样手动调用dtor?你为什么使用单身人士开始? – littleadv

+0

@littleadv boost的'serialization :: singleton'在程序退出时调用dtor,并在开始时调用ctor(初始化静态内存时)。 – Ockonal

+0

这是一个单身人士,用户*有*做你的API来破坏单身人士,通常在'main'结束之前是个好地方。明确摧毁单身人士总是最好的,这会在以后帮助你解除痛苦。 –

回答

3

对于那些单身没有退出后访问的单线程应用程序main()你可以使用一个相当简单的办法,做自动的一切:在这种情况下

Root& Root::get() { 
    static std::unique_ptr<Root> rc(new Root()); 
    return *rc; 
} 

static意味着变量在第一次调用函数并保持放置时初始化。 C++运行时安排static变量rc在某个时刻被销毁。对于在进入main()之前启动线程的多线程应用程序,您需要一种不同的方法来确保静态变量仅由线程初始化。

不过,请注意,我强烈建议聘请 -pattern 辛格尔顿(也称为全球数据)。以上代码示例不构成任何形式的推荐!在你想使用单身人士的地方有很少的有效用途,大多数用途不是。我见过的所有有效用途都使用不可变的Singleton。可变单例对象往往成为同步点,并倾向于像全局数据那样混淆使用数据。

+2

get(){static Root rc;返回rc; } - 也会起作用 – DXM

1

如果您未使用Visual Studio或C++ 11,则不会有unique_ptr <>。在这种情况下,你应该坚持使用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 
}; 
1

你考虑过如果你的单身人士根本没有被删除,会发生什么?

根据定义,单身人士是许多客户端对象共享的单个对象,因为客户往往是来来去去的。大多数情况下,你的单身人士会像你的过程一样生活。

当你的进程关闭时,操作系统将回收大部分资源(内存,文件句柄,套接字......),分配这个单例然后让它死掉是完全正常的。这将是我的建议。

  1. 该代码更简单。函数调用较少,复杂度较低。无需考虑什么时候应该终止终止,这就是你带到这里的原因。
  2. 随着应用程序变得越来越复杂(他们倾向于继续增加功能),显式清理会引入其他对象在终止和销毁后尝试访问单例引用的风险。现在,而不是无害的内存泄漏,它立即被操作系统擦除,你有一个丑陋的未处理的异常对话框,您的用户可以看看。

将单例的Initialize()函数放在GetInstance()(如pdillon3演示的)中,而不是让您的应用程序显式调用Initialize()也是一个好习惯。你没有指定这个部分,如果你的项目是一个可执行文件,你应该确保现有的设计。但是如果你把这段代码放在一个DLL里面,请小心。有些人认为DllMain是初始化单例对象的好地方,事实并非如此。在DllMain期间,加载程序锁定全局临界区被锁定,并且单例初始化容易导致各种麻烦。

现在加上现在,而不是你的单例界面中的3个方法,你只需要一个:GetInstance(),很好,很简单。

最后,作为迪特马尔提及(尽管我仍然认为辛格尔顿是一个模式,不是一个反模式和GoF同意我的看法),你真的应该考虑一下你是否真的需要一个单身。我时常使用它们,但只有当我没有选择时,不是因为它们方便。当替代品更加邪恶时,他们确实是最后的设计模式。不要仅仅因为他们方便而使用它们。