这个想法是在程序结束时删除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。但是,我的技术援助表示这种模式是错误的,尽管他无法回想起究竟是什么问题。所以我希望以前有人遇到过这个实现,并告诉我它有什么问题。
答案 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,表明对象是否已被释放?