問題
QQuickImageProvider
を使ったプログラムでは、main.cpp
内での登録方法に気を付けないとプログラム終了時に謎のエラーに遭遇することがある。
参照 → Qtを使った簡単なGUIアプリ(1) マンデルブロー集合を描いてみる
QtCreatorから実行した場合、メインウインドウを閉じるとQtCreatorのApplication Outputペインにこんなエラーが出る
20:51:15: Starting C:\git\build-QtMandelbrot-Qt_5_14_1_msvc2017_64-Release\release\QtMandelbrot.exe ...
20:51:23: The program has unexpectedly finished.
20:51:23: The process was ended forcefully.
20:51:23: C:\git\build-QtMandelbrot-Qt_5_14_1_msvc2017_64-Release\release\QtMandelbrot.exe crashed.
プログラムの動作自体は正常なのでなかなか気づかなかったが、気持ち悪いので修正したい。
原因
問題の箇所
main.cpp
FractalDrawer fractalDrawer(sizeSettings.getCanvasWidth(), sizeSettings.getCanvasHeight());
QQmlApplicationEngine engine;
engine.addImageProvider(QLatin1String("fractalDrawer"), &fractalDrawer);
engine.rootContext()->setContextProperty("fractalDrawer", &fractalDrawer);
FractalDrawer.h
class FractalDrawer : public QObject, public QQuickImageProvider
{
-
QObject
の派生クラスは、スタック上にインスタンスを確保してもメモリの管理はQtがよろしくやってくれる -
QQuickImageProvider
はQObject
の派生クラスではないので、addImageProvider()
の引数としてアドレスを渡すと、エンジン終了時にdeleteされてしまう - 今回の例では
FractalDrawer
はQObject
からも多重継承されているが、addImageProvider()
からはQQmlImageProviderBase
型として扱われる - 結果、スタックに置いてあるにも関わらずエンジン終了時にdeleteされる
-
main()
終了時にメモリ解放しようとして二重解放でエラー
修正方法
スタックに置くのをやめてヒープに置くようにした
main.cpp
auto *fractalDrawer = new FractalDrawer(sizeSettings.getCanvasWidth(), sizeSettings.getCanvasHeight());
QQmlApplicationEngine engine;
engine.addImageProvider(QLatin1String("fractalDrawer"), fractalDrawer);
engine.rootContext()->setContextProperty("fractalDrawer", fractalDrawer);
感想
- 多重継承はやっぱりやめたほうがいいかもしれない
- QObjectをC#や基本型の変数のように気楽に定義できるQtの世界観は好きなのだけど、やはりC++ベースである以上この手の隙間的な問題はいろいろありそう