例外的安全性

予備警告:例外安全性は完全な機能ではない!よくあるケースは動作するはずですが、クラスがリークしたり、クラッシュしたりする可能性があります。

Qt 自身は例外をスローしません。代わりにエラーコードが使われます。さらに、QIODevice::errorString() やQSqlQuery::lastError() のように、ユーザーから見えるエラーメッセージを持つクラスもあります。これには歴史的な理由と実用的な理由があります。例外を有効にすると、ライブラリのサイズが20%以上増加します。

以下のセクションでは、コンパイル時に例外サポートを有効にした場合の Qt の動作について説明します。

例外セーフモジュール

コンテナ

Qt のコンテナクラスは、一般的に例外に中立です。コンテナ・クラスは、その内部状態を有効に保ちながら、T その内部で発生した例外をユーザーに渡します。

QList<QString> list;
...
try {
    list.append("hello");
} catch (...) {
}
// list is safe to use - the exception did not affect it.

このルールの例外は、代入やコピーの際にスローされる可能性のある型のコンテナです。このような型では、値を返すだけでなくコンテナを変更する関数も安全ではありません:

MyType s = list.takeAt(2);

s の代入中に例外が発生した場合、インデックス2の値はすでにコンテナから削除されているが、s にはまだ代入されていない。これは回復の見込みなく失われる。

正しい書き方

MyType s = list.at(2);
list.removeAt(2);

もし代入が失敗しても、コンテナには値が残っています。

暗黙的に共有されるQtクラスは、代入演算子やコピーコンストラクタでthrowしないので、上記の制限は適用されないことに注意してください。

メモリ不足の処理

ほとんどのデスクトップOSはメモリをオーバーコミットします。これは、malloc()operator new が、割り当て時に十分なメモリがないにもかかわらず、有効なポインタを返すことを意味します。このようなシステムでは、std::bad_alloc 型の例外はスローされません。

それ以外のオペレーティングシステムでは、割り当てに失敗した場合、Qtはstd::bad_alloc型の例外をスローします。割り当てに失敗するのは、システムがメモリ不足に陥ったり、要求されたサイズを割り当てるのに十分な連続メモリがない場合です。

このルールに対する例外は文書化されている。例として、QImage コンストラクタは、十分なメモリが存在しない場合、例外をスローする代わりにnull イメージを作成します。

例外からの回復

現在のところ、Qt 内で発生した例外(メモリ不足など)から回復するためにサポートされている唯一のユースケースは、イベントループを終了し、アプリケーションを終了する前に何らかのクリーンアップを行うことです。

典型的な使用例です:

QApplication app(argc, argv);
...
int ret;
try {
    ret = app.exec();
} catch (const std::bad_alloc &) {
    // clean up here, e.g. save the session
    // and close all config files.

    return EXIT_FAILURE; // exit the application
}
...
return ret;

例外がスローされた後、ウィンドウサーバーへの接続はすでに閉じられているかもしれません。例外をキャッチした後にGUI関連の関数を呼び出すのは安全ではありません。

クライアント・コードでの例外

シグナルとスロット

Qtのシグナル・スロット接続メカニズムによって呼び出されたスロットから例外をスローすることは、スロット内で処理されない限り、未定義の動作とみなされます:

State state;
StateListener stateListener;

// OK; the exception is handled before it leaves the slot.
QObject::connect(&state, SIGNAL(stateChanged()), &stateListener, SLOT(throwHandledException()));
// Undefined behaviour; upon invocation of the slot, the exception will be propagated to the
// point of emission, unwinding the stack of the Qt code (which is not guaranteed to be exception safe).
QObject::connect(&state, SIGNAL(stateChanged()), &stateListener, SLOT(throwUnhandledException()));

スロットが通常の関数呼び出しのように直接呼び出された場合、例外が使用される可能性があります。これは、スロットを直接呼び出した場合、接続メカニズムがバイパスされるためです:

State state;
StateListener stateListener;

// ...

try {
    // OK; invoking slot directly.
    stateListener.throwException();
} catch (...) {
    qDebug() << "Handling exception not caught in slot.";
}

本ドキュメントに含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。