我的单例模板真的是单例吗?

Is my singleton template really a singleton?

本文关键字:单例吗 真的 单例模 我的      更新时间:2023-10-16

有人告诉我,我的单例模板可能不是真正的单例,因为有办法用它创建多个对象。当我问如何解决它时,我被忽略了。这就是为什么我来这里问我的单例模板类真的是单例吗?

#ifndef SINGLETON_H_
#define SINGLETON_H_
template <class T>
class Singleton
{
private:
static T* instance;
protected:
    Singleton<T>(  )
    {
    }
public:
    static T* getInstancePtr(  )
    {
        if ( instance == 0 )
            instance = new T(  );
        return instance;
    }
};
template <class T> T* Singleton<T>::instance = 0;
#endif

然后由我希望成为单例的类继承,如下所示:-

class Console : public Singleton< Console >
{
};

您已经将默认构造函数protected .派生类可以访问它,因此这将编译:

Console c1, c2;

您无法保证它是单例的一个简单原因是由于线程安全。

如果两个或多个线程同时调用 getInstancePtr,则最终可能会有两个或多个实例,具体取决于线程交换。

使用局部静态变量来实现单例模式:

template <class T>
class Singleton
{
    static T* getInstancePtr(  )
    {
        static T instance; // <-- HERE
        return &instance;
    }
};

除了更少的代码外,它还保证了线程安全。 它将在第一次调用Singleton<X>::getInstancePtr()时构造,连续的调用将获得一个实例。

或者,如果您希望每个线程一个实例,则可以改用thread_local

template <class T>
class Singleton
{
    static T* getInstancePtr(  )
    {
        thread_local T instance; // <-- HERE
        return &instance;
    }
};

我使用了与您相同的单例模板,但将其留给用户来创建私有构造函数和析构函数。用户将不得不与单例类交朋友,但它接近我想要的,并且可以用作单例。它(还不是线程安全的),但它"解决"了多实例问题。

要在多线程环境中工作,您需要不同的解决方案。必须使用特定的语言功能来确保在存在多个线程的情况下仅创建对象的一个实例。更常见的解决方案之一是使用双重检查锁定习惯用法来防止单独的线程同时创建单一实例的新实例。

好的,

除了多线程的任何问题之外,还有一种情况我可以创建两个实例。通过初始化下面的类控制台

class Console : public Singleton< Console >
{
};

这样

Console c1;

我最终得到了两个控制台实例,一个在 Singleton 类中保存的实例指针中,另一个在 c1 对象本身中。我通过将 Singleton 类更改为以下内容来解决此问题。

#ifndef SINGLETON_H_
#define SINGLETON_H_
template <class T>
class Singleton
{
private:
    static T* instance;
protected:
    Singleton<T>(  )
    {
        if ( instance == 0 )
            instance = static_cast<T*>(this);
    }
public:
    static T* getInstancePtr(  )
    {
        return instance;
    }
};
template <class T> T* Singleton<T>::instance = 0;
#endif

但是,除了多线程问题之外,我现在更加确定我的单例类不太可能导致多个实例。