这个Singleton实现有什么问题?

时间:2011-06-12 00:41:53

标签: c++ singleton

这个想法是在程序结束时删除C ++中的Singleton。我们在课堂上学到了这种实现方法:     

class Singleton
{

private:
    static Singleton* the_singleton;

protected:
    Singleton()
    {
        static Keeper keeper(this);
        /*CONSTRUCTION CODE*/
    }
    virtual ~Singleton()
    {
        /*DESTRUCTION CODE*/
    }

public:
    class Keeper
    {

    private:
        Singleton* m_logger;

    public:
        Keeper(Singleton* logger):m_logger(logger){}

        ~Keeper()
        {
            delete m_logger;
        }
    };
    friend class Singleton::Keeper;

    static Singleton* GetInstance();
    {
        if (!the_singleton)
            the_singleton = new Singleton();
        return the_singleton;
    }
};

Singleton* Singleton::the_singleton = NULL;

这个想法是,在第一次创建Singleton时,将在Singleton的C'tor中创建一个静态Keeper对象,一旦程序结束,Keeper将被销毁,反过来将破坏Singleton的实例它正指向。

现在,这个方法对我来说似乎很麻烦,所以我建议抛弃守护者类并使Singleton的实例成为getInstance方法的静态对象:

<!-- language: c++ -->

class Singleton
{

protected:
    Singleton()
    {
        /*CONSTRUCTION CODE*/
    }

    ~Singleton()
    {
        /*DESTRUCTION CODE*/
    }

public:
    static Singleton &getInstance()
    {
        static Singleton instance;
        return instance;
    }

    /*OBJECT FUNCTIONALITY*/
};

这样,Singleton是在第一次调用getInstance方法时构造的,并在程序结束后销毁。不需要那个守门员班。

我测试了它并且工作得很好 - 在正确的地方创建并销毁了Singleton。但是,我的技术援助表示这种模式是错误的,尽管他无法回想起究竟是什么问题。所以我希望以前有人遇到过这个实现,并告诉我它有什么问题。

3 个答案:

答案 0 :(得分:9)

这是错的,但你的修改没有引入问题 - 他们一直都在那里。

目前还没有强制执行单身人士财产。构造函数需要是私有的,而不是受保护的,以确保不会创建其他实例。


除此之外,原始代码在多线程场景中完全无法使用。 Yours will work beginning with C++0x


如果TA的守护对象已将全局指针重置为NULL,则他的实现将能够(在单线程环境中)在程序清理期间根据需要重新创建单例。阅读Singleton pattern in C++。但这仍然是单例的一个新实例,这几乎与在调用析构函数后使用单例一样出乎意料。

然而,他忽略了这一点,所以在守门员删除单身人士之后,他会很乐意在程序关闭期间使用无效指针。在你的情况下,即使对象的生命周期已经结束,至少内存区域仍然有效。


当然,您将使用改进版本进行标记,这只是表明该课程的目的不是教您C ++,而是TA对C ++的误解。

答案 1 :(得分:6)

根本没问题。对于这个应用程序来说,“守护者”的事情是坚定的,恕我直言。

在某些情况下可能需要保证。例如,如果Singleton必须采用构造函数参数,则静态分配它可能是不可能的。或者,如果其构造函数可能失败,则更简单的方法将不允许您恢复或重试。所以在某些情况下,可能需要做一些更复杂的事情。但在许多情况下,这是不必要的。

答案 2 :(得分:0)

  

“在你的,至少是记忆区域   即使是,仍然有效   对象的生命周期已经结束“

这是事实,但对程序执行没有多大意义 - 析构函数可能会将内存区域保留在使用它的状态,无论如何都会失败 - 例如。释放内部缓冲区。在调用析构函数之后,你永远不应该使用对象 - 如果你想从其他静态/全局对象的析构函数中使用你的单例,那么你就没有任何关于调用这些析构函数的顺序的保证。

如果你使用静态对象,并且其他一些析构函数会在它被销毁后尝试使用它,那么就无法检查单例是否仍然存在。如果使用将指针设置为NULL的keeper对象,那么在其他析构函数中,您至少可以检查NULL指针,并决定不使用单例。这样,虽然你可能会失败,但至少你不会得到神秘的“分段故障”或“访问违规”,程序将避免异常终止。

编辑: 您需要记住修改您的访问者函数,以便在您的守护者将此字段设置为NULL后再次创建对象 - 可能还有额外的静态bool,表明对象是否已被释放?