QSettings、QLPplication関連のBUGの一例
8390 ワード
問題の再現
次の簡単なプログラムを見て、何が起こるか推測できますか?
#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;
}
プログラムが終了するとクラッシュします.今日はほぼ1日かけてこのバグを特定しましたが、根本的な原因はやっと見つかりました.前のコードで再現できます.
の原因となる
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の構造関数が呼び出され,QSettingsがアクティブになる.ただし、QACpplication解析関数では、aを解析する前にqMetaType GuiHelperが0にセットされている.そして、悲劇は
質問?
QAPplicationが登録と反登録を担当している以上、なぜですか.もし私たちの前のコードでQLPplicationを使わずにQCoreApplicationを使えば、間違いはありません.これはなぜですか?
前に呼び出された2つの関数を見てみましょう.
ソースファイル:qguivariant.cpp
int qRegisterGuiVariant()
{
...
qMetaTypeGuiHelper = qVariantGuiHelper;
return 1;
}
Q_CONSTRUCTOR_FUNCTION(qRegisterGuiVariant)
int qUnregisterGuiVariant()
{
...
qMetaTypeGuiHelper = 0;
return 1;
}
Q_DESTRUCTOR_FUNCTION(qUnregisterGuiVariant)
関数は簡単ですが、ここには2つのマクロが追加されています.
# 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__;
すべてが明らかで、
このようにQLPplicationでの動作は、かえって多くのことをしており、qUnregisterGuiVariant()を事前に呼び出して、私たちの前の問題を引き起こしています.(もちろん、公式には自分の理由があるはずですが、私たちはまだよく知らないだけです)
メモ
プログラムには複数のdllダイナミックライブラリが使用され,少しQtの特色のある単例モードが使用され,結果としてバグの位置決めがかなり困難になった.
でもね、収穫は悪くないようです.