Guarded Pointers in Qt 3 and Qt 4

Материал из Wiki.crossplatform.ru

Перейти к: навигация, поиск
Image:qt-logo_new.png Image:qq-title-article.png
Qt Quarterly | Выпуск 14 | Документация

by Mark Summerfield

Guarded pointers are pointers that are automaticallyset to 0 when the object they are pointing to is deleted. This meansthat it is always safe to ask "if (p)" with a guardedpointer. This article gives an example of the use of guarded pointersand highlights the differences between the Qt 3 and Qt 4implementations.

Содержание

In general, guarded pointers are best used when you need to storepointers to QObjects over time, especially when you don't own theobject. Some programmers also make considerable use of guardedpointers as part of a "defensive programming" style of coding.

[править] Example: Lazy Dialog Creation

Let's imagine that we have an application where the user can open alarge number of modeless windows. One approach we could take is tocreate all the windows at startup but only show them when the userinvokes them. This will make application startup slower, uses a lotof memory, and if many of the windows aren't actually used, the speedand memory costs will be paid unnecessarily every time the program isrun.

Another approach is to just create and show the main window, and onlycreate and show other windows when they're invoked. This saves onstartup time and ensures that the application only uses the amount ofmemory actually needed. This approach has a few variations.

One variation is to simply hide windows that are closed and show themif they are invoked again. This maintains state between invocations(since the windows aren't destroyed), but prevents the program fromreusing the memory of a window that's no longer used.

Another variation is to destroy windows that are closed---this isusually fine for small windows that are quick to construct,but for large complex dialogs (e.g., a Preferencesdialog), the user may experience a short delay at every invocation.This variation has the benefit of minimizing memory use, but if stateis to be maintained between invocations then the programmer musthandle that explicitly themselves.

A third variation is to hide windows that are closed, and when theyare hidden to begin a timer. When the timer times out, after say, fiveminutes, the window is then destroyed. If the window is shown before ithas timed out, the timer is stopped. This variation means that if theuser is invoking a window over and over again it remains in memory andits state is preserved so everything works quickly; but if the userstops using the window it is deleted after a while and the memory itused becomes accessible. In this latter case the programmer must stillexplicitly save/restore the window's state if they want to preserveit, when the window is created or destroyed.

If either the second or third variations are used it often provesconvenient to have a member variable in the main window that holds apointer to the windows the user could invoke. Without a guardedpointer we might use the following declarations:

FindDlg *findDlg;

And we'd write code similar to this:

void MainWindow::findDlgInvoked()
{
    if (!findDlg)
        createFindDlg();
    findDlg->show();
    findDlg->raise();
    findDlg->setActiveWindow();  // Qt 4: activateWindow()
}

Unfortunately the code above is fragile since we must ensure thatfindDlg is set to 0 whenever the dialogis destroyed. If we miss a case, the program will crash if the userinvokes the dialog after the dialog is destroyed in the case we forgot.

If we use a guarded pointer we just need a single declaration:

QGuardedPtr<FindDlg> findDlg;

In Qt 4, the QGuardedPtr class has been renamed QPointer:

QPointer<FindDlg> findDlg;

Notice that we don't need to use a * in the declaration.The usage is identical:

void MainWindow::findDlgInvoked()
{
    if (!findDlg)
        createFindDlg();
    findDlg->show();
    findDlg->raise();
    findDlg->setActiveWindow();
}

This code allows us to defer creating the dialog until it is actuallyinvoked by the user, and also works if we use destructive theQt::WDestructiveClose flag (Qt::WA_DeleteOnClose in Qt 4)or a timer to destroy the dialog after it's been shown.

We don't haveto remember to set findDlg to zero when the dialog is destroyedbecause the guarded pointer takes care of that for us.

[править] Caveats and Overhead

Guarded pointers can generally be used just like standard pointers andboth kinds can be mixed freely. If you have a function that takes a QWidget * you can pass it a QGuardedPtr< QWidget> or a QPointer< QWidget> just as if they were plain QWidget pointers. This means that there's no need to declare functionsto take guarded pointer types.

There are only three caveats regarding Qt's guarded pointers. The first isthat they can only point to QObjects: This includes all Qt'swidgets and many more classes besides, so isn't a limitation inpractice. The second is that they cannot be incremented ordecremented, something that's normally only done for arrays anyway.The third is that they do not provide a solution formultithreaded programming: If you check a guarded pointer in onethread, "if (p)", and delete the object in another threadif you're unlucky with the scheduling the check will pass and thenthe other thread will get the processor and delete the object. So formultithreading, you still need a QMutex.

Some programmers were reluctant to use Qt 3's [[Qt:Документация_4.3.2/qguarded | QGuardedPtr</tt> classbecause of its overhead. Every QGuardedPtr ] used one signal--slotconnection (to connect to the target object's destroyed() signal),and one internal QObject to act as a receiver for thedestroyed() signal. In the example we gave above, even if dozensor even hundreds of windows were involved, this overhead would beinsignificant, but if the application was using QGuardedPtr fortens of thousands of QObjects the overhead would be considerable.

Qt 4's QPointer class provides the same functionality as Qt 3's QGuardedPtr, but with much less overhead. A QPointer stillrequires one signal--slot connection, but the only other overhead isa single pointer. Internally, Qt achieves this by using a special kindof signal--slot connection, where the target object is a QPointerinstead of a QObject.