编辑器使用ogre的singleton和loki的singleton,遇到了一个有趣的问题.
问题是关以一个bug的.
典型的qt的main函数如下:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
XEditor w;
w.show();
return a.exec();
}
QApplication是每个qt程序必须的.他应该最先构造,最后析构.上面代码没有问题,注意QApplication和XEditor(自定义ui类)都是在栈上定义的.
写的过程中对XEditor使用了loki的singleton类,主要是为了与其他类交互方便:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
XEditor::instance()::show();
return a.exec();
}
之后陆续添加了一些widget,list等控件,未发现问题.之后添加toolbar时,出现问题了,关闭程序时总会abort,发生在toolbar析构时.因为对qt并不熟悉,以为是自己写toolbar时错了,但仔细检查了自己qt控件的相关代码,未发现问题.而loki的singleton是很久之前加了进去,未想到这方面.
程序将近写完时,再跟这个问题.发现析构时toobar的parent已经为空了.综合Google的一些搜索确定了是QApplication提前析构了.参考项目使用的是ogre的singleton类,未出现问题.然后才想起来loki中的singleton类,是在运行期new出了实例:
inline T& SingletonHolder<T, CreationPolicy,
LifetimePolicy, ThreadingModel, MutexPolicy>::Instance()
{
if (!pInstance_)
{
MakeInstance();
}
return *pInstance_;
}
于是在main函数退出时,栈上的对象要先于堆上的对象析构.QApplication先析构,然后XEditor才析构,导致出现错误.这个错误比较隐蔽,并不一定发生abort.自己的项目是加入了toolbar才发现问题.
之前参考的项目对XEditor使用了ogre的singleton类.Ogre::singleton是在构造函数中就给静态指针赋值了.并且是本身的this指针.因此要求使用该类时必须先有一个实例.而第一段代码中就自然而然在栈上定义XEditor.
Singleton( void )
{
assert( !ms_Singleton );
ms_Singleton = static_cast< T* >( this );
}
ogre与loki对singleton的static指针赋值的阶段不同.导致的区别是当在栈上定义一个实例后,ogre的方式可以直接调用该类了.而loki的实现,却会自己又在堆中生成一个实例.然后才针对这个堆上的实例进行调用.
诚然loki的实现不需要用户使用之前必须定义一个实例,但在某些情况下(这个例子),会导致语义分离(我需要一个栈上的对象,但给我的是一个堆上的对象,且浪费了).loki的singleton的很多优点都有赖于类实例在堆上生成(主要是关于生命周期的管理).
如果不希望把出现2个singleton类实现,可以把QApplication也改为在堆上定义.
一个singleton的实现,其静态指针是在构造函数中生成还是动态需求时生成,其区别能在这里遇到,之前并没想到.