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

Les pointeurs intelligents de Qt

Les pointeurs intelligents de Qt


prcdentsommairesuivant

III. Comptez avec moi : combien y a-t-il de pointeurs intelligents dans Qt ?

III-A. Introduction

Ce vendredi 21 aot 2009, avec l'intgration de Qt pour Symbian, nous avons eu un nouveau jouet : une classe de pointeurs intelligents, QScopedPointer. Un des auteurs de la classe, Harald Fernengel, nous la prsente dans un billet sur son blog, traduit juste aprs.

Pourquoi avons-nous autant de classes de pointeurs intelligents ? Quelles sont les diffrences entre ces classes ?

Avant de pouvoir continuer plus avant, nous devons connatre les classes dont nous disposons. Les voici, dans l'ordre chronologique d'apparition.

a fait beaucoup, n'est-ce pas ?

Chaque cas a son utilit, et toutes, l'exception d'une, sont encore valables aujourd'hui.

III-B. Pointeur ou donnes partags ?

Commenons par attaquer directement une chose : lespointeurs partags sont diffrents des donnes partages.

Lors du partage de pointeurs, la valeur du pointeur et sa dure de vie sont protges par la classe de pointeurs intelligents. En d'autres mots, le pointeur est invariant. Cependant, l'objet point est hors de porte. Compltement hors de porte : on ne peut pas savoir s'il est copiable, assignable..., ou non.

Maintenant, le partage des donnes implique que la classe de pointeur intelligent sache quelque chose des donnes partages. En fait, une seule chose est importante : le fait que les donnes soient partages. Le comment n'est pas important. Le fait que des pointeurs soient utiliss pour le partage des donnes est un total hors sujet. Par exemple, peu vous importe la manire dont les classes outil de Qt sont implicitement partages ? Tout ce qui vous importe, c'est que les donnes soient partages (ainsi rduisant la consommation de mmoire), sans que cela soit apparent.

III-C. Le rfrencement des pointeurs : lger ou fort ?

La diffrence entre une rfrence forte ou lgre est l'existence ou non d'une classe de pointeur intelligent sur un pointeur donn qui garantit que l'objet ne sera pas supprim. En d'autres termes, si vous avez ce pointeur intelligent, serez-vous toujours sr qu'il restera valide ?

Quelques classes de pointeur ne le garantissent pas. Si elles ne le peuvent pas, leur seul objectif dans leur courte vie est de vous dire si l'objet a dj t dtruit ou non. Certaines classes peuvent vous proposer de transformer une rfrence lgre par une rfrence forte, en garantissant sa non destruction.

III-D. Les classes Qt de pointeurs intelligents

III-D-1. QPointer

Il s'agit d'une classe de pointeur rfrence lgre, ne partageant que la valeur du pointeur, pas les donnes. Il ne peut oprer que sur des QObject et sur ses enfants. Ajoute dans Qt 4.0, cette reprise de QGuardedPtr de Qt 3 (et de http://doc.trolltech.com/2.3/qguardedptr.html, de Qt 2) souffre d'un support peu fonctionnel de la constance, montrant ainsi son grand ge.

Un seul but : dire si, oui ou non, le QObject a t dtruit. l'inverse des versions 2 et 3, Qt 4 autorise le QObject vivre dans plusieurs threads simultanment. Ce qui signifie que QPointer possde un dfaut trs srieux : il laisse connatre l'existence d'un objet... mais sans aucune garantie quant la ligne suivante ! Par exemple, ce bout de code peut se trouver en situation dlicate.

 
Sélectionnez
QPointer<QObject> o = getObject();

// […]
if (!o.isNull())
    o->setProperty(“objectName”, “Object”);

Mme si isNull retourne faux, il n'est pas possible d'obtenir la moindre garantie quant la non destruction de l'objet avant la ligne suivante !

Par consquent, il n'est possible d'utiliser cette classe que si vous pouvez certifier, par des mthodes externes, que l'objet existe encore. Par exemple, QWidget et ses enfants ne peuvent tre crs, manipuls ou dtruits que dans le thread de la GUI. Si votre code est excut dans le thread de la GUI, ou qu'il est dans un thread bloqu, alors QPointer est scuris.

III-D-2. QSharedDataPointer

Maintenant, une jolie petite classe. En fait, c'est la plus importante de toutes les classes de pointeurs intelligents. De loin. Grce son ingniosit : elle fournit un partage implicite, avec un systme de COWthread-safe. Elle requiert que votre classe possde un membre ref, offrant une fonction ref() qui augmente le compte de rfrences, et une autre, unref(), qui l'abaisse et renvoie faux ds qu'il est nul. Si vous drivez votre classe de QSharedData, c'est trs exactement ce que vous aurez. De plus, un QSharedDataPointer a la mme taille qu'un pointeur ! Ce qui signifie que vous pouvez remplacer vos pointeurs par ce type de pointeurs intelligents sans casser la compatibilit binaire.

Cette classe est la base de toutes les classes rcentes de types de Qt, implicitement partags, thread-safe, COW, comme QNetworkProxy. Si elle n'est pas utilise dans d'autres classes de type comme QString, QByteArray ou QList, c'est que ces classes ont t dveloppes bien avant que celle-ci soit en projet. Rien n'empcherait leur portage vers ce type de pointeur intelligent.

Donc, ce type de pointeur est rfrence forte, en partageant les donnes.

III-D-3. QExplicitlySharedDataPointer

Cette classe est trs exactement semblable QSharedDataPointer. une diffrence prs : un dtachement n'est jamais caus implicitement. Avec QExplicitelySharedDataPointer, detach() doit tre explicitement appele. Ceci vous permet le dveloppement de classes de donnes partages explicitement, ce que Qt4 ne propose plus, contrairement au QMemArray de Qt3.

Cela vous permet, en sus, de mieux contrler l'opration de dtachement. En fait, si les classes outil de Qt devaient tre rimplmentes avec des pointeurs intelligents, elles utiliseraient ce type de pointeur. Utiliser QExplicitelySharedDataPointer permet de retarder le dtachement jusqu' la dernire extrmit, pour s'assurer qu'aucun accs mmoire inutile n'arrive.

III-D-4. QtPatternist::AutoPtr

C'est une classe utilise en interne au module QtXmlPatterns. Basiquement, c'est votre pointeur de base, standard, inintelligent. Ainsi, c'est une implmentation d'un pointeur forte rfrence. Pourtant, il ne le partage pas.

Au dbut, la seule raison pour laquelle cette classe existe est que le module au module QtXmlPatterns utilise intensivement les exceptions en interne. Pour survivre aux exceptions lances, en vitant les fuites de mmoire, un emballage de pointeur est indiqu.

III-D-5. QSharedPointer

Cette classe fut cre pour rpondre QtPatternist::AutoPtr. Lors du dbut de son criture, elle devait tre prte pour Qt 4.4, et remplacer la classe interne, alors perue comme une mauvaise utilisation de QExplicitelySharedDataPointer : cette dernire n'tait utilise pour partager des donnes. Elle l'tait pour partager des pointeurs. Les objets partags n'taient pas copiables. En recherchant un peu plus avant, on peut dcouvrir que cette classe est utilise de la mme manire dans QtScript, Phonon et Solid. En fait, QtScript l'avait introduite en ce but dans Qt 4.3.

III-D-6. QWeakPointer

Le compagnon de QSharedPointer implmente un contrle renforc sur le pointeur, en tant un pointeur intelligent rfrence lgre, ne partageant que le pointeur. Il travaille en tandem avec QSharedPointer : des QWeakPointer ne peuvent tre crs que depuis des QSharedPointer, les premiers laissent savoir quand les seconds sont dtruits.

Ils peuvent devenir QSharedPointer trs facilement, d'une manire thread-safe. Rcrivons le code prcdent.

 
Sélectionnez
QWeakPointer<Data> weak(getSharedPointer());

// […]
QSharedPointer<Data> ptr = weak;
if (!ptr.isNull())
    ptr->doSomething();

Ici, le QWeakPointer deviendra un QSharedPointer ou pas, mais cette dcision est thread-safe. Si l'opration russit, on peut tre sr que l'objet point ne sera pas supprim.

Une belle fonctionnalit a t ajoute dans Qt 4.6 : sa capacit traquer les QObject, sans passer par un QSharedPointer. Cela peut tre utilis pour dterminer si un QObject a dj t supprim ou pas. Ainsi, il implmente un pointeur rfrence lgre. Cela vous semble familier ? C'est normal : on peut remplacer les vieillissants QPointer par d'autres pointeurs, plus rcents, plus modernes, plus rapides, mais de taille diffrente.

III-D-7. QGuard

C'est une autre classe interne, ajoute pour remplacer le lent QPointer. Mais elle est en tat de flux : on ne sait pas encore si on va la garder, ni mme si on va l'utiliser. De toute faon, elle est interne, cela ne vous proccupe pas.

III-D-8. QScopedPointer

Voici le dernier n de la srie : il implmente un emballage non partag de pointeur forte rfrence. Il a t cr pour les besoins particuliers du portage sur la plateforme Symbian et de ses exceptions : nous avions besoin de librer les ressources sans mettre de bloc catch / try partout. Un pointeur porte rsout ce genre de problmes d'une manire trs RAII. En fait, QScopedPointer est un parfait remplaant pour QtPatternist::AutoPtr. Ils implmentent les mmes fonctionnalits, la version interne peut tre supprime.

Certains pensent que nous aurions pu utiliser QSharedPointer. En fait, non. Ce candidat prend, en mmoire, la place de deux pointeurs. Pour pouvoir garder la compatibilit binaire, il ne peut en prendre qu'un seul. C'est aussi la raison pour laquelle QScopedPointer a un destructeur personnalis en tant que paramtre template, l'oppos d'un paramtre au constructeur : il n'a pas les 4 ou 8 bytes de place pour stocker le destructeur personnalis.

De plus, QScopedPointer implmente un comptage de rfrence atomique. Ce n'est pas trs important qu'il soit atomique : le comptage de rfrence n'est pas trs utile dans les cas grs par QScopedPointer.

III-E. Et pourquoi pas le C++ 0x ? TR1 ? Boost ?

Certains ont suggr d'utiliser std::shared_ptr ou std::tr1::shared_ptr. Ces gens ne regardent pas plus loin que le bout de leur nez : nous ne pouvons pas utiliser le C++0x. Il n'est mme pas approuv, et seul deux compilateurs le supportent : GCC 4.3, et Visual C++ 10 (encore en beta !). Il n'est mme pas marrant de proposer d'utiliser le C++0x pour Qt pour le moment. Vous pouvez l'utiliser dans votre code, mais nous ne pouvons pas l'utiliser dans Qt.

TR1 est support par plus de compilateurs. Cependant, pas assez. Nous devons toujours supporter des compilateurs qui n'implmentent pas entirement C++98, et des gens qui ne veulent pas changer leurs options de compilation ! Par exemple, la dernire version de Sun Studio (Sun Studio 12, avec CC 5.10) vient toujours avec une implmentation RogueWave de la STL pr-C++98 ! Si on regarde la comparaison de Sun entre libCstd (Rogue Wave) et STLPort 4, on peut voir qu'ils vont garder une implmentation vieille de plus de 11 ans par dfaut. Mais le fait est, et nous devons travailler avec lui.

Ce qui signifie que le seul pointeur intelligent de la STL que nous pouvons utiliser est std::auto_ptr. Et mme l, il y a des problmes (la librairie de Rogue Wave n'implmente pas les templates membres).

Ce qui nous laisse Boost. Et il y a de belles choses dans Boost : boost::shared_ptr, boost::intrusive_ptr, boost::scoped_ptr, etc. En fait, il y a beaucoup de belles choses dans Boost. Trs souvent, on voit des choses que l'on aimerait avoir dans Qt.

L'un des problmes principaux de Boost, c'est son API, qui ne ressemble vraiment pas celle de Qt. En d'autres termes, une API horrible. Mais ce n'est qu'une opinion, pas un fait. Mme si l'API de Boost est intuitive pour certains, elle ne correspond pas celle de Qt. Ce qui signifie que les gens qui veulent utiliser Boost avec Qt doivent apprendre les deux conventions de nommage, les deux faons de faire...

Nous pourrions emballer toute l'API de Boost. Cependant, en continuant ainsi, on peut voir facilement une chose : Qt perd une partie importante de sa technologie. Nous devons ainsi grer n'importe quel problme qui leur incombe, leur vitesse. Aussi, cela ajoute une dpendance Qt, que nous ne pouvons pas justifier cause de la compatibilit binaire. Un autre des grands problmes.

Ainsi, Boost non plus n'est pas une solution.

III-F. Conclusion

Qt aurait-il trop de pointeurs intelligents ? Rellement ?

En fait, il n'y a que ceux-ci, en excluant les classes internes, et l'anctre QPointer :

Classe Description
QSharedDataPointer & QExplicitelySharedDataPointer Partage de donnes (implicite et explicite)
QSharedPointer Partage comptage de rfrence de pointeurs rfrence forte
QWeakPointer Partage comptage de rfrence de pointeurs rfrence faible
QScopedPointer & QScopedArrayPointer Emballage sans comptage de rfrence de pointeurs rfrence forte

prcdentsommairesuivant

Copyright © 2009 Thiago Macieira & Harald Fernengel. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.