Gestion de la mémoire dans Qt?

96

Je suis assez nouveau sur Qt et je me pose des questions sur certains éléments de base avec la gestion de la mémoire et la vie des objets. Quand dois-je supprimer et / ou détruire mes objets? Est-ce que tout cela est géré automatiquement?

Dans l'exemple ci-dessous, lequel des objets que je crée dois-je supprimer? Qu'arrive-t-il à la variable d'instance myOtherClasslorsqu'elle myClassest détruite? Que se passe-t-il si je ne supprime pas (ou ne détruit) pas du tout mes objets? Est-ce que ce sera un problème de mémoire?

MyClass.h

class MyClass
{

public:
    MyClass();
    ~MyClass();
    MyOtherClass *myOtherClass;
};

MaClasse.cpp

MyClass::MyClass() {
    myOtherClass = new MyOtherClass();

    MyOtherClass myOtherClass2;

    QString myString = "Hello";
}

Comme vous pouvez le voir, c'est assez facile pour les débutants, mais où puis-je en apprendre davantage à ce sujet de manière simple?

Martin
la source

Réponses:

100

Si vous construisez votre propre hiérarchie avec QObjects, c'est-à-dire que vous initialisez tous les QObjects nouvellement créés avec un parent,

QObject* parent = new QObject();
QObject* child = new QObject(parent);

alors il suffit de deletele parent, car le parentdestructeur se chargera de détruire child. (Il le fait en émettant des signaux, il est donc sûr même lorsque vous supprimez childmanuellement avant le parent.)

Vous pouvez également supprimer d'abord l'enfant, l'ordre n'a pas d'importance. Pour un exemple où l'ordre ne importe est ici la documentation sur les arbres d'objet .

Si vous n'êtes MyClasspas un enfant de QObject, vous devrez utiliser la manière simple C ++ de faire les choses.

Notez également que la hiérarchie parent-enfant de QObjects est généralement indépendante de la hiérarchie de la hiérarchie des classes C ++ / de l'arborescence d'héritage. Cela signifie qu'un enfant affecté n'a pas besoin d'être une sous-classe directe de son parent . Toute (sous-classe de) QObjectsuffira.

Cependant, certaines contraintes peuvent être imposées par les constructeurs pour d'autres raisons; comme dans QWidget(QWidget* parent=0), où le parent doit être un autre QWidget, en raison, par exemple, des indicateurs de visibilité et parce que vous feriez une mise en page de base de cette façon; mais pour le système de hiérarchie de Qt en général, vous êtes autorisé à en avoir QObjectcomme parent.

Debilski
la source
21
(It does this by issuing signals, so it is safe even when you delete child manually before the parent.)-> Ce n'est pas la raison pour laquelle il est sûr. Dans Qt 4.7.4, les enfants QObject sont supprimés directement (via delete, voir qobject.cpp, ligne 1955). La raison pour laquelle il est sûr de supprimer d'abord les objets enfants est qu'un QObject dit à son parent de l'oublier lorsqu'il est supprimé.
Martin Hennings
5
J'ajouterais que vous devez vous assurer que les destructeurs de descendants sont virtuels pour que cela soit vrai. Si ClassBhérite de QObjectet ClassChérite de ClassB, alors ClassCne sera correctement détruit par la relation parent-enfant de Qt que si ClassBle destructeur de est virtuel.
Phlucious
1
Le lien dans la réponse est maintenant rompu (pas surprenant après presque 4 ans ...), c'était peut-être quelque chose comme ça qt-project.org/doc/qt-4.8/objecttrees.html ?
PeterSW
2
Le destructeur de @Phlucious QObject est déjà virtuel, ce qui rend le destructeur de chaque sous-classe virtuel automatiquement.
rubenvb
1
Si une classe quelque part dans l'arborescence d'héritage a un destructeur virtuel, chaque classe enfant ci-dessous aura un destructeur virtuel. Maintenant, s'il existe une classe parent feuille en dehors d'une telle chaîne de destructeurs virtuels sans destructeur virtuel, je pense que vous pouvez avoir des problèmes si vous supprimez un pointeur vers cette classe spécifique lorsque l'objet réel est quelque part plus bas dans cette chaîne. Dans le cas d'une classe enfant de QObject et de la suppression d'un pointeur QObject vers une instance de cette classe enfant, il n'y a jamais de problème, même si vous oubliez le mot-clé virtual dans la déclaration de destructeur de cette sous-classe.
rubenvb
47

Je voudrais prolonger la réponse de Debilski en soulignant que le concept de propriété est très important dans Qt. Lorsque la classe A assume la propriété de la classe B, la classe B est supprimée lorsque la classe A est supprimée. Il existe plusieurs situations dans lesquelles un objet devient propriétaire d'un autre, pas seulement lorsque vous créez un objet et spécifiez son parent.

Par exemple:

QVBoxLayout* layout = new QVBoxLayout;
QPushButton someButton = new QPushButton; // No owner specified.
layout->addWidget(someButton); // someButton still has no owner.
QWidget* widget = new QWidget;
widget->setLayout(layout); // someButton is "re-parented".
                           // widget now owns someButton.

Un autre exemple:

QMainWindow* window = new QMainWindow;
QWidget* widget = new QWidget; //widget has no owner
window->setCentralWidget(widget); //widget is now owned by window.

Alors, vérifiez souvent la documentation, elle spécifie généralement si une méthode affectera la propriété d'un objet.

Comme indiqué par Debilski, ces règles s'appliquent UNIQUEMENT aux objets qui dérivent de QObject. Si votre classe ne dérive pas de QObject, vous devrez gérer la destruction vous-même.

Austin
la source
Quelle est la différence entre l'écriture: QPushButton * someButton = new QPushButton (); ou QPushButton someButton = nouveau QPushButton ou juste QPushButton someButton;
Martin
3
Ehh, il y a une énorme différence entre QPushButton * someButton = new QPushButton; et QPushButton someButton ;. Le premier allouera l'objet sur le tas, tandis que le second l'allouera sur la pile. Il n'y a aucune différence entre QPushButton * someButton = new QPushButton (); et QPushButton someButton = new QPushButton ;, tous deux appelleront le constructeur par défaut de l'objet.
Austin
Je suis très nouveau dans ce domaine, désolé de demander mais quelle est la différence entre «allouer l'objet sur le tas» et «allouer sur la pile»? Quand dois-je utiliser heap et quand dois-je utiliser stack? Merci!
Martin
3
Vous devez en savoir plus sur les allocations dynamiques, la portée des objets et RAII. Dans le cas du C ++ simple, vous devez allouer des objets sur la pile chaque fois que possible car les objets sont automatiquement détruits lorsqu'ils sont hors de portée. Pour les membres de la classe, il est préférable d'allouer les objets sur le tas en raison des performances. Et chaque fois que vous voulez qu'un objet "survit" à l'exécution d'une fonction / méthode, vous devez allouer l'objet sur le tas. Encore une fois, ce sont des sujets très importants qui nécessitent une lecture.
Austin
@Austin La déclaration générale selon laquelle vous devez allouer des membres de classe sur le tas pour les performances est bullocks. Cela dépend vraiment et vous devriez préférer les variables avec une durée de stockage automatique jusqu'à ce que vous trouviez un problème de purrformance.
rubenvb
7

Parent (soit l'objet QObject ou sa classe dérivée) a une liste de pointeurs vers ses enfants (QObject / son dérivé). Le parent supprimera tous les objets de sa liste d'enfants pendant que le parent est détruit. Vous pouvez utiliser cette propriété de QObject pour que les objets enfants soient supprimés automatiquement chaque fois que le parent est supprimé. La relation peut être établie à l'aide du code suivant

QObject* parent = new QObject();
QObject* child = new QObject(parent);
delete parent;//all the child objects will get deleted when parent is deleted, child object which are deleted before the parent object is removed from the parent's child list so those destructor will not get called once again.

Il existe un autre moyen de gérer la mémoire dans Qt, en utilisant smartpointer. L'article suivant décrit divers pointeurs intelligents dans Qt. https://blog.qt.digia.com/blog/2009/08/25/count-with-me-how-many-smart-pointer-classes-does-qt-have/

yesraaj
la source
-2

Pour ajouter à ces réponses, à des fins de vérification, je vous recommande d'utiliser la Visual Leak Detetorbibliothèque pour vos projets Visual C ++, y compris les projets Qt car elle est basée sur C ++, cette bibliothèque est compatible avec les new, delete, free and mallocdéclarations, elle est bien documentée et facile à utiliser. N'oubliez pas que lorsque vous créez votre propre classe d'interface QDialogou QWidgethéritée, puis créez un nouvel objet de cette classe, n'oubliez pas d'exécuter la setAttribute(Qt::WA_DeleteOnClose)fonction de votre objet.

Khaled
la source