QSettings、QApplication相关的 BUG 一例
来源:互联网 发布:零售进销存软件免费版 编辑:程序博客网 时间:2024/06/05 22:37
问题重现
看看下面这个简单的程序,能猜出会发生什么问题么?
#include <QtCore/QSettings>#include <QtGui/QApplication>#include <QtGui/QColor>class A:public QObject{public: A(QObject *parent):QObject(parent){} ~A() { QSettings settings("test.ini", QSettings::IniFormat); settings.setValue("color", QColor(Qt::red)); }};int main(int argc, char *argv[]){ QApplication app(argc, argv); A * a = new A(&app); return 0;}
该程序退出时会崩溃。今天几乎用了一天的时间来定位这个bug,不过根本原因总算找到了。并可用前面的代码进行重现。
原因
QApplication 构造与析构时会注册与反注册掉GUI相关的 Variant 类型(Font、Color等等)。
QApplication::QApplication(int &argc, char **argv){... // trigger registering of QVariant's GUI types qRegisterGuiVariant();...}QApplication::~QApplication(){... // trigger unregistering of QVariant's GUI types qUnregisterGuiVariant();}
QSetttings 将 QFont、QColor 等写入配置文件时需要这些信息,其实不止是QSettings,任何需要调用QMetaType::save的都有这个问题,将QVariant的的数据写入QDataStream流。
bool QMetaType::save(QDataStream &stream, int type, const void *data){... switch(type) {... case QMetaType::Long: stream << qlonglong(*static_cast<const long *>(data)); break; case QMetaType::Int: stream << *static_cast<const int *>(data); break;... case QMetaType::QFont: case QMetaType::QPixmap: case QMetaType::QBrush: case QMetaType::QColor: case QMetaType::QPalette:... case QMetaType::QQuaternion: if (!qMetaTypeGuiHelper) return false; qMetaTypeGuiHelper[type - FirstGuiType].saveOp(stream, data); break;}
出现问题的原因: a的parent是QApplication对象 app,所以 app 析构到最后时时将自动 delete 掉 a。此处a的析构函数被调用,QSettings 被激活。但是,在析构a之前,QAcpplication析构函数中已经将qMetaTypeGuiHelper置位0。于是,悲剧了
疑问?
既然 QApplication 负责注册与反注册,可是为什么,为什么? 如果我们前面的代码中不使用 QApplication 而使用 QCoreApplication 的话,却不会出错,这又是为何??
看看前面调用的两个函数:
源码文件:qguivariant.cpp
int qRegisterGuiVariant(){... qMetaTypeGuiHelper = qVariantGuiHelper; return 1;}Q_CONSTRUCTOR_FUNCTION(qRegisterGuiVariant)int qUnregisterGuiVariant(){... qMetaTypeGuiHelper = 0; return 1;}Q_DESTRUCTOR_FUNCTION(qUnregisterGuiVariant)
函数很简单,不简单之处在于,此处多了两个宏:
# define Q_CONSTRUCTOR_FUNCTION0(AFUNC) \ static const int AFUNC ## __init_variable__ = AFUNC();# define Q_CONSTRUCTOR_FUNCTION(AFUNC) Q_CONSTRUCTOR_FUNCTION0(AFUNC)# define Q_DESTRUCTOR_FUNCTION0(AFUNC) \ class AFUNC ## __dest_class__ { \ public: \ inline AFUNC ## __dest_class__() { } \ inline ~ AFUNC ## __dest_class__() { AFUNC(); } \ } AFUNC ## __dest_instance__;# define Q_DESTRUCTOR_FUNCTION(AFUNC) Q_DESTRUCTOR_FUNCTION0(AFUNC)
似乎有点乱,我们展开看一眼:
static const int qRegisterGuiVariant__init_variable__ = qRegisterGuiVariant();class qUnregisterGuiVariant__dest_class__ {public: \ inline qUnregisterGuiVariant__dest_class__() { } inline ~ qUnregisterGuiVariant__dest_class__() { qUnregisterGuiVariant(); }} qUnregisterGuiVariant__dest_instance__;
一切很明了,
- 构造一个static全局变量,编译器会强制 qRegisterGuiVariant() 在 main 函数之前被执行。
- 构造另一个全局对象,程序退出时,其析构函数被执行,进而调用 qUnregisterGuiVariant();
这样看来,QApplication 中的动作反而有点多此一举,而且提前调用了一次qUnregisterGuiVariant(),还导致我们前面的问题。(当然,官方这样应该有自己的理由,只是我们尚不太清楚罢了)
小记
程序中使用多个dll动态库,而且用了 有点Qt特色的单例模式,结果导致了bug定位相当困难。
不过呢,收获似乎还不错。
- QSettings、QApplication相关的 BUG 一例
- QSettings的基本使用详解(一)
- Qt下hide()与close()相关的bug一例
- QSettings 用法(一)
- Qsettings的中文处理
- QSettings的基本用法
- QSettings的简单应用
- QSettings注册表的用法
- 关于QSettings的用法
- QSettings
- qsettings
- QSettings
- QSettings
- QSettings
- QSettings
- QSettings
- QApplication的用法
- QApplication
- as3学习笔记之ConvolutionFilter滤镜
- 关于参加ACM-ICPC的 ACMer的 个人训练问题
- SQL Server2005杂谈:公用表表达式(CTE)的递归调用
- Linux内核中常见内存分配函数介绍
- openfire 用户上线和离线
- QSettings、QApplication相关的 BUG 一例
- ContentProvider完整案例
- eclipse插件开发,RCP开发常识
- 为grid设置背景色(X5中)
- java 中的gc的几种方式及各个方式使用的收集算法
- 一、Objective-C语言概述
- 匈牙利算法
- AS3的正则表达式 去除字符串首尾空格
- osip和exosip在windows下的编译静态库