3

以下代码是我对单例模式的实现。

 #include <iostream>

template<class T>
class Uncopyable
{
protected:
    Uncopyable(){}
    ~Uncopyable(){}
private:
    Uncopyable(const Uncopyable<T>&);
    Uncopyable& operator=(const Uncopyable<T>&);
};

template <class T>
class Singleton : private Uncopyable<T>
{
public:
    static T* getInstancePtr()
    {
        return instance;
    }
protected:
    Singleton<T>()
    {
        if(instance == 0)
        {
            instance = new T();
        }
    };
    ~Singleton<T>()
    {

    };
private:
    static T* instance;
};
template<class T> T* Singleton<T>::instance = 0;

class Test : public Singleton<Test>
{
public:
    Test(){};
    ~Test(){};
    inline void test() const
    {
        std::cout << "Blah" << std::endl;
    }
private:
    friend class Singleton<Test>;
protected:
};

int main(int argc, char* argv[])
{
    Test* t = Test::getInstancePtr();
    Test* t2 = Test::getInstancePtr();

    t->test();
    t2->test();

    return 0;
}

它以这种形式工作,但是我不确定它是否真的正确,因为 Singleton 的构造函数和析构函数受到保护而不是私有。如果我将它们声明为私有,则代码将无法编译,因为类无法访问它们。这个实现是否可以安全使用,或者我可以做些什么来改进它以确保只创建和使用一个实例。

谢谢

4

6 回答 6

7

那肯定是单例的错误实现。该实施存在太多问题。

在 C++11 中,您可以使用std::call_oncestd::once_flag实现单例模式。这是一个例子:

//CRTP base singleton class

template<typename TDerived>
class Singleton 
{
    static std::unique_ptr<TDerived> m_instance;
    static std::once_flag            m_once;

protected:     

    Singleton() {}

public:

    ~Singleton() { }

    static TDerived & GetInstance() 
    {
        std::call_once
        ( 
           Singleton::m_once, 
           [] (){ Singleton::m_instance.reset( new TDerived() ); }
        );
        return *m_instance;
    }
};

template<typename TDerived> 
std::unique_ptr<TDerived>  Singleton<TDerived>::m_instance;

template<typename TDerived> 
std::once_flag   Singleton<TDerived>::m_once;

现在您可以从中得出:

class Demo : public Singleton<Demo>
{
     public:
          void HelloWorld() { std::cout << "HelloWorld" << std::endl; }
};

//call HelloWorld() function through singleton instance!
DemoSingleton::GetInstance().HelloWorld();
于 2012-11-15T13:32:34.270 回答
2

您发布的代码有几处问题。

  1. 该类Uncopyable不需要模板化
  2. 该类Singleton不是线程安全的
  3. 您的Singleton实例永远不会被删除

我会将您的访问器重新实现为:

static T& GetInstance()
{
    static T instance;
    return instance;
}

然后确保调用Singleton<T>::GetInstance()应用程序的主线程(在初始化期间)以避免任何线程问题。

于 2012-11-15T13:29:09.687 回答
1

你的析构函数私有会导致编译错误?因为当进程结束时,编译不能调用私有函数所以对象不能被删除

于 2012-11-15T13:33:45.147 回答
1

不,这不是单例模式的良好实现,它不起作用!示例中唯一的 Test 实例为 NULL!构造函数永远不会被调用!

您需要将 Singleton::getInstancePtr 更改为:

public:
    static T* getInstancePtr()
    {
        if(instance == 0)
        {
            instance = new T();
        }
        return instance;
    }
protected:
   Singleton<T>() {};

现在将调用 Test 的构造函数。

于 2012-11-15T13:41:22.937 回答
0

通常单例对象在程序的生命周期内都存在,所以我不会那样实现它们,因为你使用动态分配,那么有人必须释放它,你返回一个指向你的单例对象的指针,然后你可能会不小心删除它,所以我将使用这样的东西:

template< class T >
struct Singleton : Uncopyable<T> {
public:
    static T& get_instance() {
        static T res;
        use( res ); // make sure object initialized before used
        return res;
    }
private:
    static void use( T& ) {}
};
于 2012-11-15T13:27:24.367 回答
0

C++ 中没有正确实现单例反模式。

这种尝试的主要问题是:

  • 语义非常奇怪且容易出错;您必须Singleton在某处实例化才能创建实例。您的示例从不创建实例,并且t->test()错误地通过空指针调用函数。
  • 构造不是线程安全的;如果两个未同步的线程都实例化,则可以创建两个实例Singleton
  • 如果实例是实际创建的,那么它就会被泄露。

错误较少的实现可能更像这样:

template <typename T>
T & singleton()
{
    static T instance;
    return instance;
}

但这仍然存在问题:特别是,实例可能会在其他静态对象之前被销毁,这些静态对象可能会尝试在其析构函数中访问它。

于 2012-11-15T13:32:56.023 回答