异常安全性

初步警告 :异常安全特征不完整!常见情况应该工作,但类仍可能泄漏甚至崩溃。

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.";
}