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

Discussion :

Construire une boîte de dialogue proprement

  1. #1
    Invité
    Invité(e)
    Par défaut Construire une boîte de dialogue proprement
    Bonsoir,

    J'ai construit un QDialog comprenant une QLineEdit mais je ne suis pas sûr j'ai l'impression d'y être allé comme un bourrin.

    Alors voilà : dans l'entête j'ai déclaré dans la classe MainWindow :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    QLineEdit l;
    QDialog d;
    Dans le constructeur de MainWindow :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    l.setParent(&d) // ça n'est pas un "un" mais un "elle" (l quoi)
    Et le dialogue apparaît/disparaît plus tard soit avec show()/close() ou exec() mais on s'en fiche.

    Je ne sais pas : il y a un truc qui me chiffonne quand même...

    Pensez-vous que je dois consulter ?

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    En effet, tu y es allé "comme un bourrin"

    Et tu prend de très gros risques (d'erreur de segmentation ) qui plus est.

    Si tu veux créer une boite de dialogue personnalisée, tu dois créer une classe qui représentera cette boite de dialogue et qui hérite de QDialog.

    En gros, tu dois avoir une classe qui ressemblera à quelque chose comme
    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
    class MyDialog : public QDialog{
        Q_OBJECT
        public:
            MyDialog(QWidget * parent /* = 0 */);
            /* peut etre beaucoup de choses ici ;) */
        private:
            QLineEdit * edit; // pour pouvoir le récupérer "facilement" ;)     
            /* peut être d'autres parties auxquelles tu voudras pouvoir accéder ici */
    };
     
    MyDialog::MyDialog(QWidget * parent):QDialog(parent){
        edit  = new QLineEdit;
        /* s'il y a d'autres éléments, tu voudras peut être les placer
         * dans un layout... J'utilise QVBoxLayout, mais ca peut en être un autre ;)
         */
        QVBoxLayout * newLayout = new QVBoxLayout;
        newLayout->addWidget(edit);
        setLayout(newLayout);
    }
    A partir de là, tu peux utiliser un objet de type MyDialog assez facilement, par exemple (*) sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void MainWindow::showMyDialog(){
        MyDialog dialog;
        dialog.show();
    }
    (*) d'autres solutions sont bien sur envisageables
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    Attends : je ne vois pas pourquoi on ne peut pas créer un dialogue de la sorte et pourquoi ça peut bugger... ? A mon sens ça ne sert à rien de dériver : perte de temps pour pas grand chose... Non ?

    Je crée la lineedit et le QDialog indépendemment dans l'entête et après j'assemble les 2 dans le constructeur avec setParent().

    J'ai regardé le ui_MainWindow.h (le source correspondant au formulaire graphique) et c'est bien comme ça qu'on fait, à part qu'il est utilisé l'allocation dynamique : new.

    Ce qui me choque c'est que je fais pourtant pareil mais que quand je ferme le programme avec la croix en haut à droite ça me met "envoyer ou pas le rapport d'erreur". Chose curieuse : le problème disparaît quand dans le destructeur de la MainWindow je mets ceci, juste avant le "delete ui;" :. Bizarre...

    Pour moi, on peut très bien mettre un QDialog parent d'une QLineEdit je ne vois pas en quoi ça gêne... Non ?

    Sinon, quel intérêt d'avoir crée une classe QDialog : concrètement l'objet est un carré vide : c'est la raison pour laquelle je lui rajoute une QLineEdit à laquelle j'ai connecté signaux avec slots auparavant.

    En résumé : si je prends un peu de recul, j'ai crée les 2 indépendamment, je les assemble mais le programme n'est content que si je désassemble les 2 à la fin... Comme si le programme, quand je le ferme, crois que la lineedit est indépendante du QDialog (comme au tout début) la cherche mais ne la trouve pas.

    Au secours... Qué voy a ser je suis perdu !
    Dernière modification par Invité ; 30/11/2013 à 12h14.

  4. #4
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par gizmo27 Voir le message
    Bonjour,

    Attends : je ne vois pas pourquoi on ne peut pas créer un dialogue de la sorte et pourquoi ça peut bugger... ?
    En un mot comme en 100, à cause de la gestion interne des widget.

    Toute la mécanique interne de QWidget consiste à faire en sorte que chaque widget parent va automatiquement essayer d'appeler delete sur chacun de ses enfants et ce, de manière automatique et récursive.
    A mon sens ça ne sert à rien de dériver : perte de temps pour pas grand chose... Non ?
    Une perte de temps

    Selon toi, qu'auras tu le plus facile à faire :
    Créer une classe qui ne dérive pas de QDialog, qui utilise en interne un (pointeur sur un objet de type) QDialog et créer dans ta classe toutes les fonctions qui t'intéresse en faisant en sorte qu'elles appellent celles de QDialog (au besoin avec les adaptations), ou, simplement, faire dériver ta classe de QDialog, de manière à ce qu'elle dispose d'office de toutes les fonctions qui t'intéresse et te contenter de spécialiser les quelques comportements nécessaires

    D'un coté, tu peux t'attendre à devoir créer un grand nombre de fonctions dans ta classe uniquement pour pouvoir appeler les fonctions correspondante du membre de type QDialog, de l'autre, si tu as trois comportement à adapter, tu n'as que trois fonctions à redéfinir

    Je crée la lineedit et le QDialog indépendemment dans l'entête et après j'assemble les 2 dans le constructeur avec setParent().
    Et comme QDialog va appeler delete -- lors de sa destruction -- sur le pointeur de son enfant qui correspond à ton QLineEdit, et que ce pointeur pointe vers un objet qui n'a pas créé avec new, tu vas observer un joli comportement indéfini qui va se transformer en erreur de segmentation à tous les coups.
    J'ai regardé le ui_MainWindow.h (le source correspondant au formulaire graphique) et c'est bien comme ça qu'on fait, à part qu'il est utilisé l'allocation dynamique : new.
    Mais, si tu regardes un tout petit peu d'avantage la classe en question, tu remarqueras qu'elle hérite de QMainWindow

    Et, surtout, tu remarqueras que tout ce que tu places dans la classe ui_MainWindow est géré au travers de pointeurs. Ce n'est pas une coïncidence
    Ce qui me choque c'est que je fais pourtant pareil mais que quand je ferme le programme avec la croix en haut à droite ça me met "envoyer ou pas le rapport d'erreur".
    Parce que tu n'as pas eu recours à l'allocation dynamique pour ton QLineEdit... C'est ce que je viens de t'expliquer
    Chose curieuse : le problème disparaît quand dans le destructeur de la MainWindow je mets ceci, juste avant le "delete ui;" :. Bizarre...
    Parce que tu dis explicitement à ton élément qu'il n'a plus de parent, ce qui a pour résultat de retirer le lien entre le parent et l'enfant qui a été créé lorsque tu as invoqué setParent au début.

    Mais ce n'est pas la bonne manière de pratiquer

    Pour moi, on peut très bien mettre un QDialog parent d'une QLineEdit je ne vois pas en quoi ça gêne... Non ?
    On peut le faire, à condition que l'objet de type QLineEdit ait été créé avec new.

    Mais, essaye d'invoquer setParent sur deux ou trois QLineEdit, avec un code proche de
    QLineEdit * edit1=new QLineEdit;
    QLineEdit * edit2=new QLineEdit;
    QLineEdit * edit3=new QLineEdit;
    QDialog dialog;
    edit1->setParent(&dialog);
    edit2->setParent(&dialog);
    edit3->setParent(&dialog);
    et tu m'en diras des nouvelles
    Sinon, quel intérêt d'avoir crée une classe QDialog : concrètement l'objet est un carré vide : c'est la raison pour laquelle je lui rajoute une QLineEdit à laquelle j'ai connecté signaux avec slots auparavant.
    Parce que c'est une boite vide qui propose malgré tout la possibilité d'avoir un titre et d'avoir la croix de fermeture (au minimum)

    Et, surtout, parce que c'est une classe de base qui est utilisée pour quelques boites de dialogues spécifique (QFileDialog et autres).

    Le but est de pouvoir gérer plusieurs boites de dialogues sans avoir à s'intéresser à ce qu'elles font réellement, en les considérant "simplement" comme des boites de dialogues (sans précision) et de pouvoir invoquer des comportement comme show ou comme exec qui risquent (pour exec en tout cas) de devoir s'adapter au type réel de la boite de dialogue de l'objet sur lequel la fonction est appelée (profiter du polymorphisme, en gros )

    En résumé : si je prends un peu de recul, j'ai crée les 2 indépendamment, je les assemble mais le programme n'est content que si je désassemble les 2 à la fin...
    Et c'est normal cf le début de ma réponse
    Comme si le programme, quand je le ferme, crois que la lineedit est indépendante du QDialog (comme au tout début) la cherche mais ne la trouve pas.
    Au contraire, quand tu fermes ton programme, ton QDialog est détruit, et c'est justement parce qu'il essaye de détruire un enfant que tu n'as pas créé avec new que le programme plante.

    Sans oublier dans tout cela les risques liés à la portée dans laquelle ton QLineEdit est déclaré
    Au secours... Qué voy a ser je suis perdu !
    Et si tu faisais simplement comme je te l'ai expliqué, plutot que d'essayer de faire "à ta sauce"
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #5
    Invité
    Invité(e)
    Par défaut
    Merci je vais essayer de tirer ça au clair.

    Donc dans mon cas on ne peut pas ajouter de lineedit dans un QDialog sans faire d'allocation dynamique. Question : pour quelle raison ?

    Et j'ai déjà essayé de passer par un pointeur pour ma lineedit mais c'est bien pire ! J'ai le même message d'erreur qu'à la fin sauf qu'il apparaît au début et le programme plante.

    Penses à te faire payer un gros supplément par DVP pour avoir bossé en heures supp : le samedi c'est 25% en plus

    Bon week-end à toi.
    Cordialement, Gizmo.
    Dernière modification par Invité ; 30/11/2013 à 15h53.

  6. #6
    Invité
    Invité(e)
    Par défaut
    @koala01 : en fait je me suis un peu précipité pour demander pourquoi alors que je penses avoir la réponse, qui est : c'est parce que c'est le gars qui a programmé la fonction setParent() ainsi (après tout si je veux vraiment savoir pourquoi je vais voir la fonction au détail mais je ne suis pas sûr d'en avoir le courage).

    Maintenant je suppose que si ça a été fait comme ça c'est qu'il y a une ou plusieur(s) raison(s).

    Ne connaissant pas les raisons je trouve "dommage" de rendre obligatoire la création d'une QLineEdit par allocation dynamique si on veut l'intégrer à un QDialog.

    Après je suppose que je n'ai pas à ramener ma fraise car les gars qui ont fait Qt s'y connaissent bien plus que moi et qu'ils savent très bien ce qu'ils font.

    C'est juste pour discuter voilà tout.

    Bon week-end. Cordialement, Gizmo.

    PS : et pour ton test ça marche vraiment nickel c'est juste que j'avais connecté les signaux/slots de la QLineEdit AVANT de la créer avec le new, donc forcément...

  7. #7
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par gizmo27 Voir le message
    @koala01 : en fait je me suis un peu précipité pour demander pourquoi alors que je penses avoir la réponse, qui est : c'est parce que c'est le gars qui a programmé la fonction setParent() ainsi (après tout si je veux vraiment savoir pourquoi je vais voir la fonction au détail mais je ne suis pas sûr d'en avoir le courage).

    Maintenant je suppose que si ça a été fait comme ça je suppose qu'il y a une ou plusieur(s) raison(s).

    Ne connaissant pas les raisons je trouve "dommage" de rendre obligatoire la création d'une QLineEdit par allocation dynamique si on veut l'intégrer à un QDialog.

    Après je suppose que je n'ai pas à ramener ma fraise car les gars qui ont fait Qt y connaissent bien plus que moi et qu'ils savent très bien ce qu'ils font.

    C'est juste pour discuter voilà tout.

    Bon week-end. Cordialement, Gizmo.
    Tu as tout à fait le droit de te poser la question

    Et tu as tout à fait le droit d'obtenir une réponse qui te permettra de comprendre. La voici :

    Pour commencer, il faut déjà savoir ce qui se cache derrière la fonction setParent():

    Pour faire simple, la fonction setParent() d'un objet va invoquer la fonction addItem() du parent qui lui est transmis en argument.

    En très gros, elle va prendre la forme de (*)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void QWidget::setParent(QWidget * newParent){
        /* s'il y a déjà un parent, on lui demande d'abandonner l'enfant */
        if(parent_){
            parent_->removeItem(this);
        }
        /* on indique au nouveau parent (s'il existe) qu'il a un enfant de plus */
        if(newParent)
            newParent->addItem(this);
        /* et on défini le nouveau parent comme parent de l'objet */
        parent_=newParent;
    }
    (*)J'ai utilisé mes propres noms, il y a toute une mécanique mise en place par Qt qui n'est pas prise en compte, mais le résultat est quasiment identique

    Pour comprendre pourquoi c'est fait de la sorte, il faut te rappeler cinq choses :

    1- Si tu veux pouvoir profiter du polymorphisme d'inclusion (c'est ce que l'on appelle, par abus de langage le polymorphisme ) et pouvoir gérer des QWidget -- quel que soit leur type réel -- comme s'ils étaient des QWidget, sans autre précision, tu dois les manipuler soit au travers de référence soit au travers de pointeurs.

    Si tu essayes d'affecter une instance dont le type dérive de QWidget (par exemple, un objet de type QLineEdit) à un objet (qui n'est ni une référence, ni un pointeur) de type QWidget, tu auras un effet nommé "slicing": tu perdras toutes les informations qui font de QLineEdit un QWidget "particulier".

    Et il ne faut pas oublier qu'un QLineEdit n'est jamais qu'un type de widget parmi plein d'autres! : Tous les objets visuels que tu peux placer dans une interface graphique héritent -- de manière directe ou indirecte -- de QWidget.

    Si tu perds les informations qui font d'un QLineEdit un QWidget particulier, il en ira de même pour les QComboBox, les QLabel, les QPushButton et tous les autres

    Tu imagines sans peine les problèmes que cela pourrait occasionner

    2- Lorsque tu crées un objet sans avoir recours à la création dynamique (par exemple, sous la forme de QLineEdit myEdit;), sa durée de vie s'étend de la déclaration de la variable (ici myEdit) à l'accolade fermante de la portée dans laquelle elle est déclarée.

    Ainsi, si tu écris une fonction proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    void foo(){
        QLineEdit myEdit;
        /* ... */
    } // myEdit est automatiquement détruit ici
    ton objet myEdit sera automatiquement détruit lorsqu'on atteindra l'accolade fermante sauf si myEdit est renvoyé par valeur par la fonction:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    QLineEdit foo(){
        QLineEdit myEdit;
        /* ... */
        return myEdit; //myEdit continue à exister tant que la variable qui le récupère existe
    }
    Mais je viens de t'expliquer que tu subirait un problème de slicing si tu décidais d'affecter ton QLineEdit à un objet de type QWidget.

    Tu subirais donc le phénomène de slicing de plein fouet si tu modifiait foo pour qu'elle prenne la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    QWidget foo(){
        QLineEdit myEdit;
        /* ... */
        return myEdit; // Tu renvoie un QWidget, et tu perds donc tout ce qui fait de myEdit un QLineEdit
    }
    De même, tu subirait le slicing de plein fouet avec un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    QLineEdit foo(){
        QLineEdit myEdit;
        /* ... */
        return myEdit; //myEdit continue à exister tant que la variable qui le récupère existe
    }
    void bar(){
        QWidget  obj =foo(); // Ce qui était un QLineEdit n'est maintenant plus qu'un QWidget
        /* ... */
    }
    Tu me diras sans doute que la solution serait de déclarer obj comme étant un QLineEdit.

    Ce n'est pas faux, mais c'est sans compter sur le fait que foo() peut très bien renvoyer une fois un QLineEdit et qu'une surcharge de la fonction (ou une redéfinition de son comportement, si c'est une fonction membre d'une classe et qu'elle est virtuelle) pourrait vouloir renvoyer un QPushButton, un QLabel ou n'importe quoi d'autre.

    Tu me diras alors sans doute "bah, renvoyons notre QLineEdit par référence"

    Cela nous donnerait un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    QWidget & foo(){ // QLineEdit hérite de QWidget. Il peut donc "passer pour"
                     // un objet de type QWidget
        QLineEdit myEdit;
        /* ... */
        return myEdit;
    }
    void bar(){
        QWidgt & obj=foo();
    }
    Mais tu aurais tout faux! car la durée de vie d'un objet ne s'étend au delà de l'accolade fermante de la portée dans laquelle il est déclaré que s'il est renvoyé par valeur, et non par référence.

    Obj serait donc une référence qui fait ... référence à un objet qui a déjà été détruit. Toute tentative d'accès à une fonction membre de obj dans bar a de fortes chances de se terminer en erreur de segmentation (en tout cas, c'est un comportement indéfini).

    3- La seule solution que l'on ait pour contourner le problème est d'accepter de prendre la responsabilité de la durée de vie de l'objet. Autrement dit, de recourir à l'allocation dynamique.

    Ainsi, le code suivant fonctionnera sans problème
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    QWidget * foo(){
        QLineEdit * ptr = new QLineEdit; // on prend la responsabilité de la durée de vie
                                         // de ptr
        /* ... */
        return ptr;
    }
    void bar(){
        QWidget * ptr_to = foo();
        /* on peut utiliser ptr_to ici, quel que soit le type réel de l'objet renvoyé par
         * foo
         */
        delete ptr_to; // mais il ne faut pas oublier de le détruire
    }
    A ce sujet, il faut se rappeler qu'un pointeur n'est jamais qu'une valeur numérique "tout ce qu'il y a de plus normale" à ceci près qu'elle représente l'adresse mémoire à laquelle nous trouverons un objet du type indiqué.

    foo renvoie donc par valeur une valeur numérique qui correspond à l'adresse mémoire à laquelle nous trouverons l'objet que nous avons créé

    4- On ne peut pas créer une collection de références.

    On peut créer une collection d'objets sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::vector<MyClass> tab; // sous Qt ce sera sans doute plutôt QVector, mais ce n'est pas le problème
    On peut créer une collection de pointeurs sur des objets -- car, après tout, un pointeur n'est qu'une valeur numérique -- sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::vector<MyClass *> tab;
    Mais on ne peut pas créer une collection de référence sur des objets, parce qu'une référence n'est qu'un alias de (un autre nom pour) l'objet référencé. Le code suivant sera refusé par le compilateur
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::vector<MyClass & > tab; // refusé: impossible de créer une collection de références
    5 - C++ ne fournit aucun mécanisme de garbage collector: quand le développeur décide de prendre la responsabilité de la durée de vie d'un objet en ayant recours à new, il prend explicitement la responsabilité de décider du meilleur moment pour détruire l'objet en question.

    S'il n'y fait pas attention, il lui sera facile de "perdre" l'adresse mémoire à laquelle se trouve un objet dont il a accepté de prendre la responsabilité de la durée de vie, et il n'y aura aucun moyen de la récupérer par la suite. Cela occasionnera ce que l'on appelle une "fuite mémoire".

    Si l'on perd trop souvent les adresses auxquelles se trouvent les objets dont on a la responsabilité de la durée de vie, c'est -- à terme -- la stabilité de tout le système qui sera compromise, car il arrivera un moment où l'ordinateur ne trouvera plus assez de mémoire pour faire son boulot

    Ca, c'est le décors dans lequel on travaille.

    Maintenant, il faut comprendre que nous ne sommes pas en Chine : un enfant n'aura jamais qu'un parent à la fois, mais un parent peut avoir un nombre indéterminé d'enfants: il en aura peut être 3, peut être 5 ou peut être 150... Vas savoir.

    Et surtout, chaque enfant peut être un QLineEdit, un QLabel, un QPushButton, un QMenu, un QComboBox ou ... en gros, n'importe quel type dérivé (de manière directe ou indirecte) de QWidget.

    Et comme il serait virtuellement impossible (et très coûteux en mémoire) d'avoir une collection de QLineEdit, une autre de QLabel, une troisième de QPusButton, et ainsi de suite -- on risquerait toujours d'oublier de rajouter une collection pour un type particulier, ce qui serait dommage -- la meilleure solution est de se dire que tous les enfants sont des ... QWidget.

    Et comme on ne peut pas créer de collection de références, qu'une collection d'objets provoquerait un phénomène de slicing, la seule solution qu'il nous reste, si l'on veut profiter du polymorphisme, c'est une collection de pointeurs.

    Ce qui tombe bien, parce que le pointeur est, justement, très fortement relié à l'allocation dynamique de la mémoire et que l'allocation dynamique de la mémoire est le meilleur moyen d'être en mesure de créer un objet présentant des comportements polymorphes "quelque part" et d'être en mesure de le renvoyer pour qu'il soit utilisé "ailleurs".

    Mais, quand on parle d'allocation dynamique de la mémoire, il ne faut pas oublier qu'elle va de pair avec la nécessité de décider du meilleur moment pour libérer cette mémoire!

    Qt avait donc deux solutions :

    Soit, de laisser le développeur libre de travailler de la manière qu'il voulait en le laissant totalement responsable de ses actes.

    Le développeur aurait pu créer un objet en ayant recours à l'allocation dynamique de la mémoire... ou non et transmettre l'adresse correspondante au parent, soit en ayant recours à l'opérateur addressof, soit en transmettant le pointeur vers l'objet créé au travers de new.

    Et il aurait été du ressort du développeur de veiller à la libération correcte de la mémoire de l'objet "enfant".

    Cela aurait laissé une très grande liberté à l'utilisateur, mais cela présente un risque énorme : si l'utilisateur "fait une connerie", comme celles qui consistent à utiliser une variable temporaire comme enfant, à libérer trop tôt la mémoire allouée à un objet ou à oublier de le faire, tout le système s'écrase comme un château de cartes.

    L'alternative est de se dire que le QWidget "parent" est finalement le mieux placé pour décider de quand ses enfants doivent être détruits.

    Après tout, cela semble logique, vu que les enfants devront être détruits au moment où leur parent l'est si l'on veut éviter les problèmes.

    Alors, évidemment, cela retire un peu de liberté à l'utilisateur, vu que ca le force à recourir à l'allocation dynamique, mais, d'un autre coté, cela lui apporte énormément de facilité (il n'a plus besoin de s'inquiéter de savoir quand les différents widgets deviennent inutiles) et de sécurité (il est sur que les widgets "enfants" resteront disponibles tant que le parent en aura besoin, tout en étant tout aussi sur qu'ils seront détruits "en temps et en heure").

    C'est donc un compromis entre la liberté et la sécurité des plus acceptables (beaucoup plus, en tout cas, qu'un Patriot Act américain )

    Toute la mécanique Qt se base sur ce compromis et c'est en définitive très certainement la "moins mauvaise des solutions"
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  8. #8
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    Citation Envoyé par koala01 Voir le message
    Si tu perds les informations qui font d'un QLineEdit un QWidget particulier, il en ira de même pour les QComboBox, les QLabel, les QPushButton et tous les autres

    Tu imagines sans peine les problèmes que cela pourrait occasionner
    ... Ce qui engendrerait la destruction totale de l'univers !

    Sinon merci pour l'explication (fallait pas trop te tracasser non-plus : cette histoire d'heures supp un Samedi c'était pour blaguer un peu ... Quoique je me suis toujours demandé si quand le forum changeais de nom ça n'entraînais pas des changements au niveau de l'organisation interne : contre-partie, etc...)

    Ne m'en veux pas : je n'ai pas eu le temps de tout lire (je ne me moque pas) et surtout de tout assimiler : ça sera en plusieurs "parties".

    Mais allez juste comme ça, à la louche, un renseignement de la sorte ça vaut bien dans les 20€ TTC...

    En tout cas merci pour la réponse et bonne journée.
    Cordialement, Gizmo.

  9. #9
    Invité
    Invité(e)
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    QWidget & foo(){ // QLineEdit hérite de QWidget. Il peut donc "passer pour"
                     // un objet de type QWidget
        QLineEdit myEdit;
        /* ... */
        return myEdit;
    }
    void bar(){
        QWidgt & obj=foo();
    }
    Bonsoir koala,

    (Et moi qui pensais avoir pigé les bases du c++...) Je ne comprends pas cette fonction : en quel honneur la lineedit devient-elle une référence ?

    Grosso modo comment fonctionne la fonction ? En interne (à l'intérieur des accolades de la fonction) tu lui dis de retourner myEDit qui n'est pourtant pas une référence. Et bing ! Pourtant il y a QWidget &

    Alors ça voudrait dire en gros qu'à la sortie la fonction crée une référence de type QWidget différente de la lineedit crée à l'intérieur (et qui meurt car victime de la portée ça j'ai compris) ?

    Donc à sa sortie la fonction crée une référence temporaire de type QWidget que je vais appeler "refed" (même si elle ne lui donne aucun nom c'est juste pour l'exemple) qu'elle est censée "initialiser" avec myEDit (qui est "crevée").

    En gros à la sortie de la fonction tout se passe un peu comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    QWidget & refed=myEDit_crevée // donc ça bloque
    Et moi qui croyait que c'était plus simple que ça...

  10. #10
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    En fait, une référence n'est jamais qu'un alias de l'objet référencé. (je simplifie et je laisse volontairement la représentation qui est faite d'une référence au niveau du processeur de coté )

    C'est un peu comme les pseudo sur le forum : je suis une personne en chaire et en os (surtout en os, d'ailleurs ) mais je suis essentiellement connu sous l'alias (le pseudo) koala01. Et pourtant, koala01, c'est moi! Il n'y a aucune différence entre mon identité réelle et koala01

    Du coup, tant que tu as un objet original, tu peux déclarer une référence qui servira d'alias à cet objet.

    Grosso modo comment fonctionne la fonction ? En interne (à l'intérieur des accolades de la fonction) tu lui dis de retourner myEDit qui n'est pourtant pas une référence. Et bing ! Pourtant il y a QWidget &
    Grosso modo, le code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    QWidget & foo(){ // QLineEdit hérite de QWidget. Il peut donc "passer pour"
                     // un objet de type QWidget
        QLineEdit myEdit;
        /* ... */
        return myEdit;
    }
    est en tout point similaire à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Widget & foo(){ // QLineEdit hérite de QWidget. Il peut donc "passer pour"
                     // un objet de type QWidget
        QLineEdit myEdit;
        QLineEdit & ref = myEdit; // on crée un alias (pour lequel on dispose d'un nom)
        /* ... */
        return ref;
    } // aie... myEdit est détruit ici, ref devient l'alias de quelque chose qui n'existe plus
    Alors ça voudrait dire en gros qu'à la sortie la fonction crée une référence de type QWidget différente de la lineedit crée à l'intérieur
    Non, une référence n'est jamais qu'un alias : On ne lui donne pas de nom (disons que c'est un alias abonné chez anonymous ), l'alias est, effectivement créé à l'intérieur de la fonction, mais ce n'est jamais qu'une variable anonyme.

    Et, surtout, le point important, c'est qu'on renvoie quelque chose qui passe pour être de type QWidget, mais qui est en réalité de type QLineEdit (du fait de l'héritage : un QLineEdit EST-UN QWidget "spécialisé")
    (et qui meurt car victime de la portée ça j'ai compris) ?
    Pour être précis, c'est lobjet dont on renvoie l'alias qui est victime de la portée, ce qui fait que la référence fait référence à quelque chose qui n'existe plus, mais oui, c'est bien tout le problème
    Donc à sa sortie la fonction crée une référence de type QWidget que je vais appeler "refed" (même si elle ne lui donne aucun nom c'est juste pour l'exemple) qu'elle est censée "initialiser" avec myEDit (qui est "crevée").
    Exactement
    En gros à la sortie de la fonction tout se passe un peu comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    QWidget & refed=myEDit_crevée // donc ça bloque
    Ouaip, c'est bien ca
    Et moi qui croyait que c'était plus simple que ça...
    C'est surtout très logique

    Si je me fais écraser demain en rue et que j'en meure, d'ici un an, les gens trouveront sans doute encore mes différentes interventions sur le forum, mais plus personne n'aura l'occasion de me rencontrer

    une fois que tu as compris ce qu'est une référence, le reste "coule de source"
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  11. #11
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    En tout cas j'aurais appris encore une chose ce soir-là.

    Tu ne m'en veux pas mais je préfère de loin ce code-là :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Widget & foo(){ // QLineEdit hérite de QWidget. Il peut donc "passer pour"
                     // un objet de type QWidget
        QLineEdit myEdit;
        QLineEdit & ref = myEdit; // on crée un alias (pour lequel on dispose d'un nom)
        /* ... */
        return ref;
    }
    Le fait de créer la référence à l'intérieur de la fonction est pour moi beaucoup plus explicite car jusqu'à maintenant j'ai toujours cru que la variable retournée était la même et non qu'elle était temporairement stockée dans une autre (même si j'ai compris ça me paraît un peu abstrait encore).

    Et pourtant la fonction append() de l'incontournable classe QString renvoie bien une référence (qui à mon humble avis n'est pas un alias du néant)...
    Ca serait une référence vers un objet créer par new ? Dans ce cas pourquoi ne pas retourner un pointeur ?
    Non c'est bizarre...

  12. #12
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par gizmo27 Voir le message
    Bonjour,

    En tout cas j'aurais appris encore une chose ce soir-là.

    Tu ne m'en veux pas mais je préfère de loin ce code-là :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Widget & foo(){ // QLineEdit hérite de QWidget. Il peut donc "passer pour"
                     // un objet de type QWidget
        QLineEdit myEdit;
        QLineEdit & ref = myEdit; // on crée un alias (pour lequel on dispose d'un nom)
        /* ... */
        return ref;
    }
    Le fait de créer la référence à l'intérieur de la fonction est pour moi beaucoup plus explicite car jusqu'à maintenant j'ai toujours cru que la variable retournée était la même et non qu'elle était temporairement stockée dans une autre (même si j'ai compris ça me paraît un peu abstrait encore).
    C'est équivalent, mais de toutes manières incorrect

    Simplement, ce code est effectivement peut être plus explicite alors que, autrement, tu as affaire à une référence "anonyme"
    Et pourtant la fonction append() de l'incontournable classe QString renvoie bien une référence (qui à mon humble avis n'est pas un alias du néant)...
    Ca serait une référence vers un objet créer par new ? Dans ce cas pourquoi ne pas retourner un pointeur ?
    Non c'est bizarre...
    La fonction append renvoie "ce qui est pointé par" le pointeur this.

    Autrement dit, une référence sur la chaine de caractères au départ de laquelle on a invoqué la fonction append.

    Et cet objet, il existe avant la fonction, il n'est donc pas détruit quand on quitte la fonction append

    Le fait de renvoyer une référence vers un objet (quel que soit le type) permet surtout d'enchaîner les appels.

    C'est, par exemple, parce que la signature de l'opérateur << est
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ostream & operator << (ostream & os, T const & item);
    que tu peux écrire un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::cout<<"salut " << variable1 <<" "<<variable2;
    (note que l'on retrouve le même principe dans l'opérateur d'affectation quand on le définit soi même )

    Le fait que la fonction append() renvoie une référence sur la QString au départ de laquelle elle est appelée permet d'écrire un code proche
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    QString str;
    str.append( /* ... */ )
       .append( /* ... */ )
       .append( /* ... */ );
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  13. #13
    Invité
    Invité(e)
    Par défaut
    Merci pour tout ces renseignements.
    Bon week-end.

Discussions similaires

  1. FAQ - imprimer une boîte de dialogue
    Par Eugénie dans le forum MFC
    Réponses: 6
    Dernier message: 27/08/2004, 13h34
  2. [MFC] afficher une boîte de dialogue
    Par bigboomshakala dans le forum MFC
    Réponses: 13
    Dernier message: 10/05/2004, 14h22
  3. [Kylix] Imprimer le contenu d'une boîte de dialogue
    Par cmp-france dans le forum EDI
    Réponses: 13
    Dernier message: 18/10/2003, 20h31
  4. Réponses: 3
    Dernier message: 29/08/2003, 10h57

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