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

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Qt Discussion :

Comprendre la gestion facile de mémoire par Qt - fuite de mémoire


Sujet :

Qt

  1. #1
    bruce-willis
    Invité(e)
    Par défaut Comprendre la gestion facile de mémoire par Qt - fuite de mémoire
    Bonjour les amis !
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <qlcdnumber.h>
    #include <qslider.h>
     
    #include "mywidget.h"
     
    MyWidget::MyWidget( QWidget *parent, char *name ) : QVBox( parent, name )
    {
      QLCDNumber *lcd = new QLCDNumber( this );
      QSlider *s = new QSlider( QSlider::Horizontal, this );
     
      connect( s, SIGNAL(valueChanged(int)), lcd, SLOT(display(int)) );
    }
    La plupart des programmeurs de C++ réagiront à ce code, nous faisons un new, mais aucun delete. Il est fait par la classe de base de laquelle nos widgets sont dérivés. Mais je ne comprends pas, comment ? Je pense pas que MFC de Visual C++ ne permet pas de faire ça, donc c'est une spécificité de Qt ?
    QMyObject est ici une classe héritant QObject, pourquoi mon code provoque une fuite de mémoire tandis que mon second n'en fait pas :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    int main( int argc, char **argv )
    {
      // Create an application
      QApplication a( argc, argv );
     
      // Create instances
      QMyObject top( 0, "top" );
      QMyObject *x = new QMyObject( &top, "x" );
      QMyObject *y = new QMyObject( &top, "y" );
      QMyObject *z = new QMyObject( x, "z" );
     
      // Do stuff to stop the optimizer
      top.doStuff();
      x->doStuff();
      y->doStuff();
      z->doStuff();
     
      return 0;
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    int main( int argc, char **argv )
    {
      // Create an application
      QApplication a( argc, argv );
     
      // Create instances
      QMyObject top( 0, "top" );
      QMyObject *x = new QMyObject( 0, "x" );
      QMyObject *y = new QMyObject( 0, "y" );
      QMyObject *z = new QMyObject( x, "z" );
     
      // Do stuff to stop the optimizer
      top.doStuff();
      x->doStuff();
      y->doStuff();
      z->doStuff();
     
      return 0;
    }

  2. #2
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Citation Envoyé par bruce-willis Voir le message
    QMyObject est ici une classe héritant QObject, pourquoi mon code provoque une fuite de mémoire tandis que mon second n'en fait pas :
    Bonjour,
    pourquoi dit tu que tu as une fuite memoire???

    Pour les delete, c'est Qt qui s'en charge. Lors de la création d'un objet Qt,il y as toujours un parent associé qui va géré la vie de cette objet

  3. #3
    Alp
    Alp est déconnecté
    Expert éminent sénior

    Avatar de Alp
    Homme Profil pro
    Inscrit en
    Juin 2005
    Messages
    8 575
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2005
    Messages : 8 575
    Points : 11 860
    Points
    11 860
    Par défaut
    Citation Envoyé par Matthieu Brucher
    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 possiblité de passer un objet parent au constructeur.

  4. #4
    Expert éminent sénior

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 750
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : pdg

    Informations forums :
    Inscription : Juin 2003
    Messages : 5 750
    Points : 10 667
    Points
    10 667
    Billets dans le blog
    3
    Par défaut
    Le parent détruit ses enfants.
    Un enfant détruit se désenregistre de son parent dans le destructeur.
    Si tu détruits le parent, il va détruire en cascade ses fils.

    Ce qui explique pourquoi le code suivant pose probleme:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
      QMyObject *top = new QMyObject( 0, "top" );
      QMyObject x ( top, "x" );
      delete top; // provoque un delete x...

  5. #5
    bruce-willis
    Invité(e)
    Par défaut
    Salut tout le monde !

    Donc si je retiens bien, un composant Qt déclaré dans une classe Qt doit avoir this comme paramètre pour pouvoir être détruit sans delete.

    Que se passe-t-il si je mets un delete dans le destructeur de la classe pour désallouer le composant ?

  6. #6
    Expert éminent sénior

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 750
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : pdg

    Informations forums :
    Inscription : Juin 2003
    Messages : 5 750
    Points : 10 667
    Points
    10 667
    Billets dans le blog
    3
    Par défaut
    J'ai pas compris ta phrase

    La regle est simple:
    S'il y un new, il faut un delete
    Dans le cas de Qt, il y a ce post scriptum:
    Qt effectue automatiquement un appel de delete pour les classes contenues dans un conteneur
    Donc toutes tes classes Qt que tu crées avec un parent, elles seront automatiquement détruites via un appel a delete.

    Probleme: dans l'exemple que je donne, volontairement tordu, la classe x n'a pas été allouée avec new, donc d'apres la 1ere regle ci dessus il ne faut pas faire de delete. C'est équivalent au code suivant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    QMyObject x;
    QMyObject *p_x = &x;
    delete p_x; // aie!
    Si tu désalloues toi meme un composant, ce composant va se désenregistrer de son conteneur, et donc le conteneur n'essaiera pas de le détruire une deuxieme fois. Voici un exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    class MyWidget : public QWidget
    {
    public:
        MyWidget():
            button4( this ),
            button5( 0 )
        {
             button1 = new QPushButton( this );
             button2 = new QPushButton( 0 );
             button3 = new QPushButton( this );
        }
     
        ~MyWidget()
        {
             delete button1; // facultatif
             delete button2; // obligatoire
             // button3 sera détruit par Qt dans le destructeur de QObject
        }
     
    private:
        QPushButton *button1;
        QPushButton *button2;
        QPushButton *button3;
        QPushButton button4;
        QPushButton button5;
    };
    button1: alloué avec new, en donnant un parent Qt. Du coup si je n'avais pas appelé moi meme delete dans le destructeur de ce parent, il aurait quand meme été effectué un tout petit peu plus tard dans le destructeur de QObject (je crois), exécuté une fois que ~MyWidget(), ~QWidget, etc... aient été exécuté.
    Qt ne fera pas un double "delete button1", parce que button1, quand il est détruit, va "s'enlever" de MyWidget.

    button2: il n'a pas de parent, donc Qt n'a aucun moyen de savoir quand le détruire, donc il ne le fera pas. Il faut donc soi meme faire un delete.

    button3: idem que button1, sauf que cette fois on laisse Qt effectuer le delete

    button4 et button5: sont des objets membres de MyWidget. C++ va automatiquement les détruire quand MyWidget est détruit. Ces objets vont alors "s'enlever" de MyWidget, et MyWidget n'essaiera donc pas de faire un delete dessus.

  7. #7
    bruce-willis
    Invité(e)
    Par défaut
    Si je retiens bien alors :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    button1 = new QPushButton( this );
    Inutile de faire appel à delete sauf si on veux anticiper la destruction !
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    button2 = new QPushButton( 0 );
    Il faut utiliser un delete.

    C'est là que je ne comprends pas, comment peut-on en C++ effectuer le delete d'un composant qui a comme paramètre à la construction son parent (this). Ou est-ce une spécificité de Qt ?

  8. #8
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    button2 = new QPushButton( 0 );
    ne fournie pas un parent par défaut ??

  9. #9
    Membre confirmé

    Inscrit en
    Octobre 2007
    Messages
    234
    Détails du profil
    Informations forums :
    Inscription : Octobre 2007
    Messages : 234
    Points : 644
    Points
    644
    Par défaut
    Non.
    Dans la doc, pour QObject::QObject ( QObject * parent = 0 ) :
    Setting parent to 0 constructs an object with no parent. If the object is a widget, it will become a top-level window.

  10. #10
    Expert éminent sénior

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 750
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : pdg

    Informations forums :
    Inscription : Juin 2003
    Messages : 5 750
    Points : 10 667
    Points
    10 667
    Billets dans le blog
    3
    Par défaut
    Citation Envoyé par bruce-willis Voir le message
    Si je retiens bien alors :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    button1 = new QPushButton( this );
    Inutile de faire appel à delete sauf si on veux anticiper la destruction !
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    button2 = new QPushButton( 0 );
    Il faut utiliser un delete.

    C'est là que je ne comprends pas, comment peut-on en C++ effectuer le delete d'un composant qui a comme paramètre à la construction son parent (this). Ou est-ce une spécificité de Qt ?
    Je ne comprends pas ta question. Tu le delete quand tu veux. Il s'enlevera de son parent lors de sa destruction, et il n'y aura donc aucun probleme. Si tu ne le delete pas, le parent le fera quand lui meme sera détruit.

  11. #11
    Expert éminent sénior

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 750
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : pdg

    Informations forums :
    Inscription : Juin 2003
    Messages : 5 750
    Points : 10 667
    Points
    10 667
    Billets dans le blog
    3
    Par défaut
    Je viens de me poser une question, et apparement le code de Qt n'est pas protégé contre le fait se s'attribuer soi meme comme parent:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    int main(int argc, char *argv[])
    {
         QCoreApplication app(argc, argv);
     
         QObject *obj = new QObject();
         obj->setParent( obj );
         delete obj;
     
         return app.exec();
    }
    variante:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    int main(int argc, char *argv[])
    {
         QCoreApplication app(argc, argv);
         {
             QObject obj;
             obj.setParent( &obj );
         }
         return app.exec();
    }

    Testé avec Qt 4.3.0. Quelqu'un peut tester avec la derniere version ?

  12. #12
    bruce-willis
    Invité(e)
    Par défaut
    Désolé, je travaille sous Qt 4.2 !

    La gestion mémoire de Qt commence à s'éclaircir pour moi. En fait, j'ai posé ces questions pour ne pas seulement l'utiliser mais comprendre le fonctionnement interne !!
    Est-ce qu'une telle gestion de mémoire est-elle aussi supportés par MFC ou C++ Builder par hasard ?

  13. #13
    Membre confirmé

    Inscrit en
    Octobre 2007
    Messages
    234
    Détails du profil
    Informations forums :
    Inscription : Octobre 2007
    Messages : 234
    Points : 644
    Points
    644
    Par défaut
    Sous Qt 4.3.2 on peut toujours se déclarer soi même comme parent.

  14. #14
    Expert éminent sénior

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 750
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : pdg

    Informations forums :
    Inscription : Juin 2003
    Messages : 5 750
    Points : 10 667
    Points
    10 667
    Billets dans le blog
    3
    Par défaut
    Est-ce que ca s'exécute sans planter?

  15. #15
    Membre confirmé

    Inscrit en
    Octobre 2007
    Messages
    234
    Détails du profil
    Informations forums :
    Inscription : Octobre 2007
    Messages : 234
    Points : 644
    Points
    644
    Par défaut
    J'avais fait le test sur une kubuntu (avec Qt 4.3.2 / gcc 4.1.3) et ça plante, pour les deux exemples, avec QObject qui me prévient qu'il a détecté un double delete.

    Par contre je vient de refaire ce test sur une gentoo (Qt 4.3.2 / gcc 4.1.2) et là, seul le 2ème plante. Le premier rentre sans problème dans le app.exec();

    Au début j'avais pensé que cette différence venait des options de compilations générées par qmake qui étaient différentes, mais j'ai mis la même chose pour les deux systèmes ( -pipe -g -Wall -W -D_REENTRANT -DQT_CORE_LIB -DQT_SHARED ) et ça n'a pas changé le comportement.
    En fait, après avoir un peu regardé plus en détail, ça doit venir du fait que Qt n'a pas été compilé avec debug sous Gentoo.

    Donc en résumé, je dirais que :
    Dans le premier exemple lors du delete, sous kubuntu on a un ASSERT qui échoue (ASSERT failure in QList<T>::operator[]: "index out of range") et le programme s'arrête. Sous gentoo l'ASSERT n'est pas levé mais il ne doit pas faire le double delete car il ne plante pas.
    Dans le second, aucun ASSERT n'est utilisé et donc il y a un beau plantage sur les deux systèmes car il doit vraiment faire le delete deux fois.

  16. #16
    doccpu
    Invité(e)
    Par défaut
    QObject a; //créé en pile et détruit à la fermeture de l'aplication (ou a la sortie de la fonction)

    QObject* b = new QObject(0); // créé en tas, 0 = NULL, pas de parent, doit être deleté explicitement
    QObject* c = new QObject(b); // créé en tas, b est le parent de c et le détruira à sa propre destruction (fonctionnement interne à Qt). On peux aussi deleter c explicitement, il se desenregistre alors de son parent automatiquement.

    vérifier toutes fois le nombres d'objets contenus dans b apres un :
    delete c;

Discussions similaires

  1. Gestion de la mémoire par Qt
    Par Kaluza dans le forum Qt
    Réponses: 16
    Dernier message: 31/08/2011, 15h48
  2. Réponses: 2
    Dernier message: 12/09/2010, 20h29
  3. Gestion de la mémoire par la VM
    Par sloshy dans le forum Général Python
    Réponses: 3
    Dernier message: 13/09/2009, 03h14
  4. Gestion images opencv python par mémoire partagée
    Par Tchef dans le forum Bibliothèques tierces
    Réponses: 0
    Dernier message: 12/08/2009, 15h12
  5. Gestion de la mémoire par jonas
    Par DevServlet dans le forum JOnAS
    Réponses: 2
    Dernier message: 13/01/2009, 18h01

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo