C++ 单例模式设计问题

4
我有一个需求,需要在任何时刻只有一个类的实例存在。Singleton是显而易见的选择。
但是我有一些条件并不是典型的Singleton所能满足的。
1. Singleton的生命周期并不是整个程序的生命周期。这个对象必须在我进入特定状态时创建,并且在离开状态时销毁。在整个状态期间,我不能创建类的另一个实例。
2. 每次进入状态并创建新的实例时,我需要向Singleton传递一个变量。这是一个数字,基于用户的选择。
因此,我的实现具有以下静态函数 -
// To be called exactly once, everytime I enter the state
void MySingleton::CreateInstance(size_t count);

// To be called any no. of times during the state
MySingleton * MySingleton::GetInstance();   

// To be called exactly once, when leaving the state.
void MySingleton::DestroyInstance();        

现在这种实现方式与传统的单例实现大相径庭。

这种实现方式是否存在问题?

有没有更好的替代方案?

6个回答

12

如果您每次进入某个状态时创建一个实例,并在离开该状态时销毁它,那么更合理的做法是将该实例归属于管理状态转换的实体(或其他了解状态转换的实体)。

例如,您可以在状态管理器的成员变量中拥有一个智能指针,用于引用一个实例。当您进入状态时,可以将其初始化为新实例;当您离开该状态时,可以销毁该实例。

在我看来,这比使用单例设计(反)模式更加清晰和可取。


也许这也是我的解决方案。另一种可能性是将那个管理器设计为单例,但仅当此解决方案不适用时。通常情况下,如果可以避免使用单例模式,则应该尽量避免使用它。 - Emile

5

这种实现方式有问题吗?

有的。问题在于,它不是单例模式,因为它并不符合"单例"模式的定义。它只是一个简单的变量,局限于你所进入和离开的状态。

事实上,单例模式只是一种被夸大其词的全局变量,在应用程序的整个生命周期中都存在,并且应该尽可能避免使用。你的"单例"不是全局的,而是状态局部的。


我同意“Glorified Global”这个说法...但是我完全没有使用它们来代替全局变量封装逻辑时遇到任何问题,因为多个模块需要使用相同的数据。不过我同意这种模式在这里并不适用。 - San Jacinto
+1 是为了将问题归纳到其本质:变量仅在状态中是局部的,甚至可能是指定范围。 - Paul Nathan
2
@San Jacinto:我很乐意同意Singleton通常比显式全局变量更好。通常情况下,你不应该使用Singleton或全局变量。 - David Thornley

2
您所描述的概念可以由以下类来实现: ```html

您所描述的概念可以通过以下类来确保:

```
template<class Guarded>
class single_instance : private boost::noncopyable {
   static bool alive;
public:
   single_instance() {
      if (alive)
         throw std::runtime_error("instance already exists");
      alive = true;
   }
   ~single_instance() {
      alive = false;
   }
};

template<class Guarded>
bool single_instance<Guarded>::alive = false;

如果您从这个类继承,将确保您始终只能同时创建一个实例:
class myclass : single_instance<myclass> {
public:
   myclass(size_t count);
   ...
};

现在,您可以像普通类一样实现和实例化myclass,无需使用工厂函数和getInstance方法。在第一个实例被销毁之前尝试实例化第二个副本将会抛出异常。


有时候,Boost并不是答案(例如,在我工作的环境中,我们没有能够使用当前工具链编译Boost的环境,而且我们也不会仅仅为了好玩而放弃该工具链)。请注意,我并没有投反对票……只是我更希望看到一个能够与当前标准兼容的实现。 - San Jacinto
3
@san: "noncopyable" 直接翻译成私有的、未定义的复制构造函数和赋值运算符。不过这样说太啰嗦了。当你看到 boost::noncopyable 时,可以想象它是这样实现的:private: T(const T& other); /* not defined */ T& operator=(const T&other); /* not defined */ - Bill
@San:正如Bill所说,boost::noncopyable只是一种方便的方法。您也可以通过将复制构造函数设为私有(并且为了完整性,可能还要将赋值运算符设为私有)来手动确保类无法被复制。答案的主要重点是模板的其余部分,而不是从noncopyable继承。 - sth

1

我完全同意“单例只是另一种全局变量”的说法,应该避免使用。

如果你的老板坚持要用,你可以尝试类似这样的方法:

class MyUtil
{
    protected:
    static MyUtil* m_Singleton = NULL;
    public:
    /// Create the singleton if does not exist.
    /// \return the singleton for this class.
    static GetSingleton()
    {
        if( !m_Singleton  )
        m_Singleton  = new MyUtil();
        return m_Singleton ;
    }

    /// release singleton instance
    /// Warning: Multi-threading not supported.
    void StateRelease()
    {
        m_Singleton  = NULL;
        delete this;
    }
};

1

如果它是每个状态一个,那么就从你的状态对象中创建它,并将构造函数设为私有的,友元状态。


0

我认为这个解决方案在性能时间上很昂贵。我会创建一个实例,并将创建函数更改为:

void MySingleton::reset(size_t count);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接