Revision control

Copy as Markdown

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page exceptionsafety.html
\title Exception Safety
\ingroup best-practices
\ingroup explanations-basics
\brief A guide to exception safety in Qt.
\b {Preliminary warning}: Exception safety is not feature complete!
Common cases should work, but classes might still leak or even crash.
Qt doesn't use exceptions to report errors. Instead, functions return a
value that indicates failure, such as \c false from QFile::open(), and
classes let you query the error afterward, for example with
\l QIODevice::errorString() or \l QSqlQuery::lastError(). Some classes
also emit a signal, such as QNetworkReply::errorOccurred(). Qt avoids
exceptions for historical and practical reasons.
Qt throws exceptions in a few cases, such as when a memory allocation
fails, as described in \l {Out of Memory Handling}. QFuture also passes
on exceptions that a \l{Qt Concurrent} task throws.
The following sections describe how Qt behaves when you compile your
application with exception support.
\section1 Exception Safe Modules
\section2 Containers
Qt's \l{container classes} are generally exception neutral. They pass any
exception that happens within their contained type \c T to the user
while keeping their internal state valid.
Example:
\code
QList<QString> list;
...
try {
list.append("hello");
} catch (...) {
}
// list is safe to use - the exception did not affect it.
\endcode
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:
\code
MyType s = list.takeAt(2);
\endcode
If an exception occurs during the assignment of \c s, the value at index 2 is already
removed from the container, but hasn't been assigned to \c s yet. It is lost
without chance of recovery.
The correct way to write it:
\code
MyType s = list.at(2);
list.removeAt(2);
\endcode
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.
\section1 Out of Memory Handling
In general, Qt doesn't handle out-of-memory conditions. Qt code assumes
that memory allocations succeed, and the behavior of the application is
undefined if one fails.
QList, QString, and QByteArray check their memory allocations with the
\l Q_CHECK_PTR macro, which throws a \c std::bad_alloc exception if you
compile your application with exception support. Without exception
support, running out of memory is undefined behavior.
Some operating systems, such as Linux in its default configuration,
overcommit memory. On such systems, \c malloc() or \c{operator new} can
return a valid pointer even though there isn't enough memory available,
and the system can terminate a process later when memory runs out.
Allocations can still fail, for example if the requested size is very
large.
Some classes report failed allocations in other ways, and document how.
For example, QImage constructors create a \l{QImage::isNull()}{null}
image instead of throwing an exception if there isn't enough memory.
\section1 Recovering from Exceptions
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:
\code
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;
\endcode
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.
\section1 Exceptions in Client Code
\section2 Signals and Slots
Throwing an exception from a slot invoked by Qt's \l{Signals & Slots}{signal-slot}
connection mechanism is considered undefined behaviour, unless it is handled within the slot:
\code
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()));
\endcode
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:
\code
State state;
StateListener stateListener;
// ...
try {
// OK; invoking slot directly.
stateListener.throwException();
} catch (...) {
qDebug() << "Handling exception not caught in slot.";
}
\endcode
*/