FAQ Qt FAQ Qt Creator FAQ PyQt & PySide

FAQ QtConsultez toutes les FAQ

Nombre d'auteurs : 25, nombre de questions : 299, dernière mise à jour : 15 octobre 2016 

 
OuvrirSommaireGénéralités techniquesModèle objet de QtQObject

Dans un code Qt4, on verra souvent un new sans delete associé.

En fait, si une nouvelle instance de QObject est créée et qu'on lui spécifie un parent - le premier argument du constructeur -, c'est ce parent qui sera chargé de la destruction du fils.

La majorité des classes de Qt4 hérite plus ou moins directement de QObject, mais attention, ce n'est pas le cas de toutes. L'indicateur est la possibilité de passer un objet parent au constructeur.

Mis à jour le 7 juillet 2009  par Matthieu Brucher

Lien : Allouer sur le tas ou sur la pile des QObject et dérivés ?

Cette erreur se produit lorsque la partie QObject d'une classe n'a pas été ajoutée à l'édition des liens.

Lors de l'utilisation de la macro Q_OBJECT, on définit un certain nombre de méthodes et de variables statiques. Ces méthodes et ces variables sont implémentées dans un fichier généré automatiquement par qmake à l'aide de l'outil moc.

Pour corriger le problème, ajoutez la macro Q_OBJECT dans votre déclaration de classe puis relancez qmake. Si le problème subsiste, vérifiez que le dossier du fichier source est bien présent dans les INCLUDEPATH du fichier de projet pour que le moc passe bien sur les fichiers y étant présents.

Mis à jour le 7 mai 2012  par Matthieu Brucher, Louis du Verdier

Lien : La classe QObject
Lien : Les signaux et slots

Chaque QObject contient une liste des pointeurs vers les QObject fils. Cette liste permet d'effacer automatiquement les objets fils de cette liste.

Cette liste implique que les objets fils héritant de QObject devraient être alloués sur le tas et non la pile. A priori, le code d'effacement est fait de telle sorte qu'il ne devrait pas y avoir de problème en allouant un objet sur la pile car la destruction d'un objet entraîne sa suppression dans la liste des parents. En revanche, effacer manuellement ou automatiquement par destruction dans la pile les objets fils peut entraîner un surcoût. Enfin, étant utilisé avec une sémantique de pointeurs, il vaut mieux utiliser les pointeurs.

 
Sélectionnez
{
    QObject parent;
    QObject *enfant = new QObject(&parent);
}
// enfant est correctement supprimé de la mémoire par parent

De même, une instance héritée de QObject ne peut appartenir à deux QObject.

 
Sélectionnez
    QObject *parent = new QObject;
    QObject *enfant = new QObject(parent);

    QObject autreParent;
    enfant->setParent(&autreParent); // change le parent de enfant

    delete parent;
    // enfant n'est pas supprimé car parent ne "possède" plus enfant

La relation parent-enfant peut poser problème lorsque les QObject sont alloués sur la pile. Lorsqu'un objet est alloué sur la pile, et a un parent, le parent pourrait appeler delete sur l'objet qui est sur la pile.

 
Sélectionnez
{
    // !!! exemple de ce qu'il NE faut PAS faire, ne pas recopier !
    QPushButton button("button");
    QWidget widget;
    button.setParent(&widget);
} // crash ici

// lorsque widget est détruit à la fin du scope, il supprime button à l'aide de delete.
// button avait été alloué sur la pile
Mis à jour le 7 mai 2012  par Matthieu Brucher, Benjamin Poulain

Lien : QObject
Lien : Le modèle QObject

L'héritage multiple de QObject n'est pas possible. En effet, l'architecture de Qt - les méta objets - rend la chose impossible.

Lorsque vous faites un héritage multiple, mettez en premier la classe héritant de QObject.

Mis à jour le 7 juillet 2009  par Matthieu Brucher

QObject (et toutes les classes qui en dérivent) ne se marient pas très bien avec les pointeurs intelligents classiques du C++, tels que auto_ptr ou shared_ptr. Comme expliqué dans la question Un new sans delete ?, Qt implémente viaQObject un mécanisme de gestion de la mémoire dont le principe est qu'un QObject parent détruise automatiquement tous ses objets QObject enfants.

Ce mécanisme est fort pratique et permet de grandement simplifier la gestion de la mémoire. Cependant, il entre en conflit avec tout autre mécanisme de libération de la mémoire tel que ceux implémentés au moyen de pointeurs intelligents.

En effet, si vous déclarez un pointeur intelligent sur une instance de QObject, ce dernier aura en charge la destruction de cette instance. Si par malheur votre QObject se trouve être l'enfant d'un autre QObject, et que ce parent vient à être détruit, votre instance sera elle aussi automatiquement détruite, alors que votre pointeur intelligent continuera de pointer vers elle ! Et quand votre pointeur intelligent estimera qu'il est temps de la détruire, il effectuera un appel à delete sur un objet déjà détruit (double utilisation de delete) avec toutes les conséquences fâcheuses que l'on connaît.

À noter que spécifier un parent nul au moment de la construction de votre QObject ne vous garantit pas pour autant que ce dernier ne sera pas par la suite reparenté via la fonction setParent(), chose qui se produit assez régulièrement entre objets graphiques (QWidget).

En conséquence, il est déconseillé d'utiliser des pointeurs intelligents sur des objets de type QObject. Si jamais vous devez néanmoins y avoir recours, vous devez protéger votre instance d'une double deletion au moyen d'un guarded pointer (QPointer en Qt 4, ou QGuardedPtr en Qt 3), comme dans l'exemple suivant.

 
Sélectionnez
shared_ptr<QPointer<QWidget> > ptr( new QWidget() );

La particularité d'un guarded pointer est qu'il est automatiquement mis à zéro si l'instance associée est détruite. Ainsi, dans cet exemple, quand shared_ptr effectuera un appel à delete sur une instance déjà détruite, cela reviendra à effectuer un delete sur un pointeur nul, c'est-à-dire à ne rien faire du tout.

Ayez aussi à l'esprit que shared_ptr perd son sens originel et se comporte davantage comme un weak_ptr, c'est-à-dire que vous devez systématiquement vous assurer qu'il ne soit pas nul avant de l'utiliser.

Vu la lourdeur de cette écriture et le changement de sémantique introduit, on comprend mieux pourquoi il est déconseillé d'employer des pointeurs intelligents sur des objets dérivant de QObject.

Mis à jour le 7 mai 2012  par Aurélien Régat-Barrel

Lien : Faq C++ : pointeurs intelligentsFaq C++ : pointeurs intelligents
Lien : Tutoriel : Pointeurs intelligents

Faire un delete sur un QObject peut être très dangereux. Par exemple, avec le système de signaux et de slots, il est parfois difficile d'être sûr qu'il n'est pas utilisé par la pile d'appel au moment du delete : on veut détruire un bouton quand on clique dessus.

 
Sélectionnez
class widget : public QWidget
{
Q_OBJECT
QPointer<QPushButton>  m_but ;
public :
    widget ()
    {
        m_but = new QPushButton(this);
        connect(m_but, SIGNAL(clicked()), this, SLOT(butClicked()));
    }
public slots :
    void butClicked()
    {
        delete m_but;
        m_but = 0;
    }
} ;

Lorsque l'on appuie sur le bouton, le signal clicked est émis, ce qui appelle directement le slot butClicked(). À ce niveau, la pile d'appel correspond à :

 
Sélectionnez
widget => butClicked()
m_but => clicked()

Ainsi, le bouton sur lequel on veut faire un delete est utilisé par la pile d'appel. Le delete va produire une erreur mémoire et votre application se retrouve en état indéterminé.

Pour cela, QObject fournit la fonction deleteLater, qui va poster un événement et le QObject sera détruit par l'eventloop dès que tous danger est écarté.

La fonction devient tout simplement :

 
Sélectionnez
void butClicked()
{
    m_but->deleteLater();
}

Il est intéressant de constater que cette fonction est un slot et peut donc être connecté à un signal.

Il est aussi possible d'appeler plusieurs fois cette fonction sans aucun danger.

Mis à jour le 7 mai 2012  par Yan Verdavaine

Lorsque l'on utilise un QObject (ou un enfant) en membre d'une classe, on peut se demander s'il faut utiliser un pointeur ou non. Voici ce que je vous conseille.

  • Objet optionnel : Si l'objet est optionnel, il est fortement conseillé d'utiliser le pointeur intelligent QWeakPointer (ou QPointer pour Qt avant 4.6) qui va permettre de savoir si le QObject pointé existe.
  • Les objets partagé entre classes : Les QObject ne sont pas prévus pour être utilisée par plusieurs classes en même temps. Donc cela ne devrait pas se poser.
  • Les QWidget : Comme ils peuvent être reparentés (par les layouts par exemple), il est fortement conseillé d'utiliser des pointeurs pour garantir leur destruction de manière correcte.
  • Les autres : Au choix. Il peut être intéressant de ne pas utiliser de pointeur pour éviter de faire les allocation à la main. Ceci ne pose aucun problème avec le système de destruction parent/enfant, car ils seront détruit avant la classe. Toute fois il ne faut pas oublier de mettre this en parent.
 
Sélectionnez
class monObj : QObject
{
QTimer m_timer;
public :
    monObj ()
    : m_timer(this)
    {
    }
}
 
Sélectionnez
class monObj : QObject
{
QTimer m_timer;
public :
    monObj ()
    {
        m_timer.setParent(this);
    }
}

Il est très important que tous les QObject en membre d'une classe aient pour parent this. QWidget est un cas particulier où le parent dépend de son niveau dans l'arbre parent/enfants des widgets et où le parent est forcément un QWidget.

Mis à jour le 7 mai 2012  par Yan Verdavaine
  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2006 - 2016 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.