異常安全性

初步警告 :異常安全特徵不完整!常見情況應該工作,但類仍可能泄漏甚至崩潰。

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.
					

Exceptions to that rule are containers for types that can throw during assignment or copy constructions. For those types, functions that modify the container as well as returning a value, are unsafe to use:

MyType s = list.takeAt(2);
					

If an exception occurs during the assignment of s , the value at index 2 is already removed from the container, but hasn't been assigned to s yet. It is lost without chance of recovery.

The correct way to write it:

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

If the assignment throws, the container will still contain the value; no data loss occurred.

Note that implicitly shared Qt classes will not throw in their assignment operators or copy constructors, so the limitation above does not apply.

處理內存不足

Most desktop operating systems overcommit memory. This means that malloc() or operator new return a valid pointer, even though there is not enough memory available at allocation time. On such systems, no exception of type std::bad_alloc is thrown.

On all other operating systems, Qt will throw an exception of type std::bad_alloc if any allocation fails. Allocations can fail if the system runs out of memory or doesn't have enough continuous memory to allocate the requested size.

Exceptions to that rule are documented. As an example, QImage 構造函數將創建 null image if not enough memory exists instead of throwing an exception.

從異常恢復

Currently, the only supported use case for recovering from exceptions thrown within Qt (for example due to out of memory) is to exit the event loop and do some cleanup before exiting the application.

Typical use case:

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

After an exception is thrown, the connection to the windowing server might already be closed. It is not safe to call a GUI related function after catching an exception.

在客戶端代碼中的異常

信號和槽

Throwing an exception from a slot invoked by Qt's 信號-槽 connection mechanism is considered undefined behaviour, unless it is handled within the slot:

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()));
					

If the slot was invoked directly, like a regular function call, exceptions may be used. This is because the connection mechanism is bypassed when invoking slots directly:

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