web-dev-qa-db-fra.com

Pourquoi les exemples et tutoriels officiels de Qt n'utilisent-ils pas de pointeurs intelligents?

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.

67
Martin

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).

  1. Vous devez fournir un suppresseur personnalisé qui appellera deleteLater
  2. Votre qobjet avec les parents a déjà une référence dans l'objet parent. Vous savez donc qu'un objet n'est pas divulgué tant que le parent existe. Lorsque vous devez vous en débarrasser, vous pouvez appeler directement deleteLater.
  3. Votre QWidget sans parent a déjà un référence dans l'objet Qapplication . Même chose que le point 2.

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.

60
UmNyobe

Pointeurs intelligents pour les enfants

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!

Pointeurs bruts vers les orphelins

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.

Sur une note plus générale

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.

22
Ralph Tandetzky

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.

13
Sombrero Chicken

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.

10
PRIME
  • 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.

5
g24l