Pourquoi les exemples et tutoriels officiels sur la bibliothèque Qt n'utilisent-ils jamais de pointeurs intelligents? Je ne vois que new
et delete
pour créer et détruire les widgets.
J'ai cherché la raison mais je ne l'ai pas trouvée, et je n'en vois pas moi-même sauf si c'est pour des raisons historiques ou pour une compatibilité descendante: tout le monde ne veut pas que le programme se termine si un constructeur de widget échoue, et le gère via try/catch blocs est tout simplement laid (même s'il est utilisé à quelques endroits). Le fait que les widgets parent puissent prendre possession des enfants ne m'explique que partiellement la chose, car il faudrait encore utiliser delete
pour les parents à un certain niveau.
Parce que Qt s'appuie sur un modèle parent-enfant pour gérer les ressources Qobject. Il suit le modèle composite + chaîne de responsabilité, qui est utilisé de la gestion des événements à la gestion de la mémoire, au dessin, à la gestion des fichiers, etc.
En fait, essayer d'utiliser un QObject dans un pointeur partagé\unique est une ingénierie excessive (99% du temps).
deleteLater
.Cela dit, vous pouvez toujours utiliser RAII avec Qt. Par exemple QPointer se comporte comme une référence faible sur un QObject
. J'utiliserais QPointer<QWidget>
plutôt que QWidget*
.
note: pour ne pas sonner trop fanboy, deux mots: Qt + valgrind.
Les classes de pointeur intelligent std::unique_ptr
Et std::shared_ptr
Sont destinées à la gestion de la mémoire. Avoir un pointeur si intelligent signifie que vous propre le pointeur. Cependant, lors de la création d'un QObject
ou d'un type dérivé avec un parent QObject
, la propriété (la responsabilité de nettoyer) est transférée au parent QObject
. Dans ce cas, les pointeurs intelligents de bibliothèque standard sont inutiles, voire dangereux, car ils peuvent potentiellement provoquer une double suppression. Oui!
Toutefois, lorsqu'un QObject
(ou un type dérivé) est créé sur le tas sans parent QObject
, les choses sont très différentes. Dans ce cas, vous ne devez pas simplement tenir un pointeur brut, mais un pointeur intelligent, de préférence un std::unique_ptr
Vers l'objet. De cette façon, vous gagnez en sécurité des ressources. Si vous remettez ultérieurement la propriété de l'objet à un parent QObject
, vous pouvez utiliser std::unique_ptr<T>::release()
, comme ceci:
auto obj = std::make_unique<MyObject>();
// ... do some stuff that might throw ...
QObject parentObject;
obj->setParent( &parentObject );
obj.release();
Si ce que vous faites avant de donner à votre orphelin un parent lève une exception, vous auriez une fuite de mémoire si vous utilisiez un pointeur brut pour contenir l'objet. Mais le code ci-dessus est à l'abri d'une telle fuite.
Ce n'est pas un conseil C++ moderne d'éviter les pointeurs bruts tous ensemble, mais d'éviter posséder les pointeurs bruts. Je pourrais ajouter un autre conseil C++ moderne: N'utilisez pas de pointeurs intelligents pour les objets qui appartiennent à une autre entité de programme.
Vous avez déjà répondu à votre propre question: except if it's for historic reasons/backward compatibility
. Une bibliothèque aussi énorme que QT ne peut pas supposer que tous ceux qui utilisent la bibliothèque ont des compilateurs qui prennent en charge C++ 11. new
et delete
sont garantis pour exister dans les normes antérieures.
Cependant, si vous avez le support pour utiliser des pointeurs intelligents, j'encourage à les utiliser sur des pointeurs bruts.
En plus de ce que @Jamey a dit:
Si vous le concevez intelligemment, vous n'aurez peut-être jamais besoin de supprimer un widget. Disons que vous avez une fenêtre principale et que vous en créez un objet automatique et exécutez cette fenêtre dans la boucle d'événements. Maintenant, reposez tous les éléments de ce widget peuvent être ajoutés comme ses enfants. Et puisque vous les ajoutez à cette MainWindow directement/indirectement en tant qu'enfant, lorsque vous fermerez cette fenêtre principale, tout sera pris en charge automatiquement. Il vous suffit de vous assurer que tous les objets/widgets dynamiques que vous avez créés sont des enfants/petits-enfants de la fenêtre principale. Par conséquent, pas besoin d'une suppression explicite.
QObject
a un parent défini et la structure arborescente du programme permet de gérer la mémoire assez efficacement.
Le dynamisme de Qt brise cet idéal de Nice, par exemple passant un pointeur brut autour. On peut facilement finir par détenir un dangling pointer
, mais c'est un problème courant en programmation.
Le pointeur intelligent Qt, en fait une référence faible, est QPointer<T>
et fournit une partie des bonbons STL.
On peut aussi mélanger avec std::unique_ptr
et similaires, mais il ne doit être utilisé que pour les machines non Qt de votre programme.