IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Exception Safety

A guide to exception safety in Qt.

Article lu   fois.

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

Exception Safety

Preliminary warning: Exception safety is not feature complete! Common cases should work, but classes might still leak or even crash.

Qt itself will not throw exceptions. Instead, error codes are used. In addition, some classes have user visible error messages, for example QIODevice::errorString() or QSqlQuery::lastError(). This has historical and practical reasons - turning on exceptions can increase the library size by over 20%.

The following sections describe Qt's behavior if exception support is enabled at compile time.

Exception Safe Modules

 

Containers

Qt's container classes are generally exception neutral. They pass any exception that happens within their contained type T to the user while keeping their internal state valid.

Example:

 
Sélectionnez
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:

 
Sélectionnez
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:

 
Sélectionnez
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.

Out of Memory Handling

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 constructors will create a null image if not enough memory exists instead of throwing an exception.

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:

 
Sélectionnez
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;

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.

Exceptions in Client Code

 

Signals and Slots

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

 
Sélectionnez
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:

 
Sélectionnez
State state;
StateListener stateListener;

// ...

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

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+