问题重现
看看下面这个简单的程序,能猜出会发生什么问题么?
#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定位相当困难。
不过呢,收获似乎还不错。
分享到:
相关推荐
Qt之解决QSettings中文乱码问题源码,win10,MinGw32编译通过,问题正常解决
QSettings在存取配置文件时,存在写延时问题,有时断电会出现参数丢失,先改用封装Windows下的WritePrivateProfileStringA函数来替代QSettings的相关操作。
QSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSettingsQSett
QT读写配置文件之QDataStream和QSettings,
代码演示了使用QSettings创建、设置和读取ini文件的过程。
本资源代码是进行PyQt5的QSettings测试代码,详细描述可以看我的文章:PyQt5随笔:QSettings 的简单使用详说,进行软件的设置状态数据储存与初始化。压缩文件包括test.py,testui.py两个python文件。
//向ini文件的第一个节写入内容,ip节下的第一个参数11.12.//向ini文件的第一个节写入内容,ip节下的第二个参数13.14.//向ini文件的第二个
QT学习之如何读写配置文件
在Qt里可以使用`QSettings`来实现,QSettings类提供一个独立于平台的应用程序设置,Qt已经封装好,修改、读取用户的环境变量不需要管理员权限,并且修改也是直接针对系统的环境配置进行修改,并非当前进程有效(所以...
QT INI文件 参数保存 QSettings类
对QT配置文件管理类进行封装,提高效率;统一管理各配置项默认值;避免不必要的文件读写和混乱的本地缓存。
Qt实战案例之利用QSettings读写ini配置文件,本案例介绍利用QSettings实现Qt读写ini配置文件,Qt5、Qt6可完美运行,可参考文章:https://wendy.blog.csdn.net/article/details/124949978
写这类的原因的是每次操作QSettings的指针时候,用完都要delete指针,比较麻烦,可以使用类的析构函数自动删除。还有写入值的时候,QSettings类的成员函数setValue每次都会覆盖之前的值,因此自己写的类成员函数,...
PyQtConfig是一个简单的API,用于在PyQt应用程序中处理,持久和同步配置。 特征 一致的界面,可从小部件读取值,始终get和set 无缝处理来自QSettings字符串的类型转换 集成映射处理显示和内部设置之间的转换,例如...
快应用开发
注册表的修改与添加删除等操作,MFC环境下的操作与实现.
用QSettings写注册表 用QString读注册表 用QSettings写ini文件 用QSettings读ini文件
我的这个绝对可用,比网上流传的哪个好的多,哪个有很多Bug http://www.lupaworld.com/tutorial-view-aid-9612.html 许多Windows应用程序都具有在文件选单中显示历史文件的功能,这使用户很容易再次访问这些文件。...
QSettings* settings=new QSettings("./setting.ini",QSettings::IniFormat); if(!QFile("./setting.ini").exists()) { settings->setIniCodec(QTextCodec::codecForName("utf-8")); settings->setValue("host...