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

Langage C++ Discussion :

Classes et pointeurs intelligents


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Homme Profil pro
    Etudiant
    Inscrit en
    Janvier 2016
    Messages
    54
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Etudiant

    Informations forums :
    Inscription : Janvier 2016
    Messages : 54
    Par défaut Classes et pointeurs intelligents
    Bonjour à tous,

    Actuellement sur un petit développement Qt, je suis contraint de me plonger dans les pointeurs intelligents.

    Et bien qu'ayant parcouru beaucoup de posts sur le sujet, je ne trouve pas d'explications qui me conviennent.

    J'aurais donc besoin de quelques réponses à ce sujet.
    Mais avant voici le contexte :
    - J'utilise une bibliothèque de lecture de carte RFID
    - Quelques lignes pour vous "expliquer" un peu comment fonctionne la chose :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    std::shared_ptr<logicalaccess::ReaderConfiguration> readerConfig(new logicalaccess::ReaderConfiguration());
     
    readerConfig->setReaderProvider(logicalaccess::LibraryManager::getInstance()->getReaderProvider("PCSC"));
     
    const logicalaccess::ReaderList readerList = readerConfig->getReaderProvider()->getReaderList();
     
    readerConfig->setReaderUnit(readerList.at(2));
     
    std::shared_ptr<logicalaccess::Chip> chip = readerConfig->getReaderUnit()->getSingleChip();
    Donc - un readerConfig qui "contient" un ReaderProvider et un ReaderUnit (définit en fonction du dit ReaderProvider)

    - et une carte


    Je me pose les questions suivantes :
    --> Si je souhaite stocker mon ReaderConfig et ma carte dans une classe, dois-je le stocker sous le type logicalaccess::ReaderConfiguration ou std::shared_ptr<logicalaccess::ReaderConfiguration> ?

    --> Si je souhaite l'utiliser dans une autre classe que celle où je l'ai définit, il me faudra d'abord créer une instance de la classe et utiliser un getter de cette classe

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Reader *reader = new Reader;
    "MonNouveauReaderConfig" = reader->getCurrentReaderConfig();
    Si "MonNouveauReaderConfig" est un shared_ptr, je ne peut pas simplement lui retourner mon premier shared_ptr. Comment faire ?
    J'ai pris conaissance de la fonction make_shared mais je peine à comprendre comment l'utiliser...


    --> Si j'assigne un ReaderUnit à mon premier shared_ptr de cette façon :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    readerConfig->setReaderUnit(blablabla);
    Est-ce que "MonNouveauReaderConfig" de mon autre classe possèdera également ce ReaderUnit que je viens de lui assigner ?


    Pourquoi l'utilisation d'un new pour déclarer readerConfig alors que la carte est définit grâce à une fonction contenu dans la classe ReaderConfig ? Cela fait-il une différence car lors de mes expérimentations sur make_shared, j'ai pu simplement l'appliquer sur readerConfig, mais pas sur chip.

    Voilà voilà.... ça fait beaucoup de questions mais elles sont assez basiques je pense. J'ai seulement du mal à comprendre les shared_ptr dans leur globalité et leur utilisation dans des classes étant nouveau pour moi, ça n'arrange pas les choses.

    Merci d'avance à tout ceux qui sauront m'apporter des réponses, ou même une simple piste

  2. #2
    Expert confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 499
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 499
    Par défaut
    Actuellement sur un petit développement Qt, je suis contraint de me plonger dans les pointeurs intelligents.
    Attention, les pointeurs "à la Qt" et les pointeurs intelligents sont hautement incompatible.
    Ne tentez pas d'utiliser des pointeurs intelligents sur des types de Qt.

    En n'utilisant correctement, vous n'utilisez JAMAIS "new".
    A la place de vos new, utilisez "make_shared".

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::shared_ptr<logicalaccess::ReaderConfiguration> readerConfig(std::make_shared<logicalaccess::ReaderConfiguration>());
    --> Si je souhaite stocker mon ReaderConfig et ma carte dans une classe, dois-je le stocker sous le type logicalaccess::ReaderConfiguration ou std::shared_ptr<logicalaccess::ReaderConfiguration> ?
    En "std::shared_ptr<logicalaccess::ReaderConfiguration>" car il est très peu probable que vous disposiez d'un constructeur de copie public dans la classe "logicalaccess::ReaderConfiguration".

    --> Si je souhaite l'utiliser dans une autre classe que celle où je l'ai définit, il me faudra d'abord créer une instance de la classe et utiliser un getter de cette classe
    Non.

    Est-ce que "MonNouveauReaderConfig" de mon autre classe possèdera également ce ReaderUnit que je viens de lui assigner ?
    Oui, c'est une sémantique de pointeur.

    Pourquoi l'utilisation d'un new pour déclarer readerConfig
    C'est une erreur, il faut utiliser "make_shared".

    J'ai seulement du mal à comprendre les shared_ptr dans leur globalité et leur utilisation dans des classes étant nouveau pour moi, ça n'arrange pas les choses.
    L'intérêt des pointeurs intelligents, c'est de correctement gérer "l'ownership" des pointeurs : qui est en charge d'appeler le "delete" de l'objet pointé.
    Avec "shared_ptr", tous ceux qui possèdent un "shared_ptr" sur un objet en sont les copropriétaires.
    C'est à la destruction/libération du dernier "shared_ptr" sur l'objet que celui-ci est détruit.

  3. #3
    Membre averti
    Homme Profil pro
    Etudiant
    Inscrit en
    Janvier 2016
    Messages
    54
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Etudiant

    Informations forums :
    Inscription : Janvier 2016
    Messages : 54
    Par défaut
    Une réponse rapide et précise... Merci !


    Je n'avais pas pris connaissance des pointeurs Qt... J'ai cependant eu la chance de ne pas tomber sur des pointeurs de type Qt.
    Je garde tout de même ceci dans un coin de ma tête. Quelque chose me dit que ça me sera utile sous peu.




    Je ne sais pas ce qu'est un constructeur de copie mais, effectivement, rien ne semble effectuer la "conversion" dans la classe ReaderConfiguration.




    Un simple "make_shared" dans la seconde classe suffit donc ?
    Reprenez moi si je me trompe mais cela signifie-t-il que, dans les attributs privés de la classe 1 ou dans une fonction de la classe 2, les deux "déclarations" sont des "copropriétaires" d'un objet ReaderConfiguration qui est créé lors de la première déclaration d'un shared_ptr ?

    Au premier abord, je voyez ça comme ça :
    le premier shared_ptr est créé dans les attributs de la classe 1
    on instancie la classe 1 afin d'avoir toujours un shared_ptr empêchant la destruction
    on effectue une copie du premier shared_ptr pour l'utiliser
    on crée des "copropriétaires" lors d'utilisations dans les fonctions d'autres classes

    Je me rend bien compte que ce raisonnement manque de logique et qu'il est plein d'incohérences, cependant je me perd quant à la portée des pointeurs :
    je sais bien qu'ils persistent tant qu'il en reste au minimum 1 comme tu l'as très bien expliqué plus haut mais pour un pointeur dans les attributs d'une classe, si la classe n'est pas instancié, il ne devrait pas exister, si ?)

    Ou alors, je ne dois pas le déclarer dans les attributs mais la première déclaration se fera alors dans une fonction et, à la fin de la scope, la seul copie de l'objet sera supprimée et l'objet avec lui. Je ne pourrais donc pas le récupérer dans un autre endroit de mon programme...




    Le new provient d'un précédent programme que j'avais effectué sous Visual Studio. Je l'ai bêtement copié sans trop réfléchir mais je veillerai alors à faire disparaître toute ces immondicités !


    Encore pas mal de questions mais je pense que je commence à voir la lumière dans tout ça !

  4. #4
    Expert confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 499
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 499
    Par défaut
    Je ne sais pas ce qu'est un constructeur de copie mais, effectivement, rien ne semble effectuer la "conversion" dans la classe ReaderConfiguration.
    https://cpp.developpez.com/faq/cpp/?...cteur-de-copie

    Un simple "make_shared" dans la seconde classe suffit donc ?
    Pas besoin de faire plusieurs "make_shared", il faut même éviter.

    "make_shared", c'est en gros, un new avec tout ce qu'il faut autour pour gérer des erreurs dans les constructeurs.
    Donc appeler plusieurs fois "make_shared", c'est créer plusieurs objets différents et donc pas de partages de propriétés.

    Un "std::shared_ptr" a un constructeur de copie, il est donc très facilement clonable et celui qui obtient un clone du "std::shared_ptr" devient un copropriétaire de l'objet pointé par le "std::shared_ptr" initial (et aussi par le clone, aussi bien que par les clones du clone, etc...). Donc l'objet créé est toujours valide tant qu'il est accessible via un "std::shared_ptr" (clone ou pas).

    Reprenez moi si je me trompe mais cela signifie-t-il que, dans les attributs privés de la classe 1 ou dans une fonction de la classe 2, les deux "déclarations" sont des "copropriétaires" d'un objet ReaderConfiguration qui est créé lors de la première déclaration d'un shared_ptr ?
    Non, vous avez 2 objets, chacun propriété soit de l'objet de type classe1 soit de la fonction de la classe 2.
    Si le pointeur est une variable locale de la fonction de la classe 2, il sera libéré à la fin de la méthode, et comme il est le seul propriétaire de l'objet pointé (vu qu'il n'a pas été cloné), l'objet pointé sera libéré en fin de méthode.

    le premier shared_ptr est créé dans les attributs de la classe 1
    OK, on parle bien de champs non statiques de la classe 1 ?

    on instancie la classe 1 afin d'avoir toujours un shared_ptr empêchant la destruction
    La destruction de quoi ?
    Tant que vous laissez un shared_ptr sur un objet, il ne sera pas libéré. Mais si l'objet contenant un champ de type shared_ptr est libéré, et que ce shared_ptr est le dernier sur l'objet pointé, l'objet pointé sera aussi libéré, c'est toute l'élégance des shared_ptr.

    on effectue une copie du premier shared_ptr pour l'utiliser
    Pas besoin, vous pouvez utiliser l'original, il s'use pas quand on s'en sert.

    on crée des "copropriétaires" lors d'utilisations dans les fonctions d'autres classes
    Quand vous passez un shared_ptr en paramètre, vous passez un clone. Quand vous affectez un champ de type shared_ptr depuis un autre, vous clonez l'autre, etc...
    La copropriété, c'est totomatique.

    cependant je me perd quant à la portée des pointeurs :
    Je pense que vous êtes encore trop près des pointeurs nus dans votre manière de penser les pointeurs.

    mais pour un pointeur dans les attributs d'une classe, si la classe n'est pas instancié, il ne devrait pas exister, si ?)
    Vous parlez d'un champ statique ou d'un champ d'instance ?
    Si c'est un champ statique, c'est à vous de l'instancier.
    Si c'est un champ d'objet, il est initialisé à pointer sur nullptr, donc pas de problème lors de sa destruction.

    Ou alors, je ne dois pas le déclarer dans les attributs mais la première déclaration se fera alors dans une fonction et, à la fin de la scope, la seul copie de l'objet sera supprimée et l'objet avec lui. Je ne pourrais donc pas le récupérer dans un autre endroit de mon programme...
    ???
    Le plus simple, ne serait-il pas de le fournir dans les paramètres du constructeur, ou de l'initialiser dans le constructeur ???

  5. #5
    Membre averti
    Homme Profil pro
    Etudiant
    Inscrit en
    Janvier 2016
    Messages
    54
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Etudiant

    Informations forums :
    Inscription : Janvier 2016
    Messages : 54
    Par défaut
    Pas besoin de faire plusieurs "make_shared", il faut même éviter.

    "make_shared", c'est en gros, un new avec tout ce qu'il faut autour pour gérer des erreurs dans les constructeurs.
    Donc appeler plusieurs fois "make_shared", c'est créer plusieurs objets différents et donc pas de partages de propriétés.
    Un seul make_shared "initial" donc, puis une copie du premier à chaque réutilisation.

    Un "std::shared_ptr" a un constructeur de copie, il est donc très facilement clonable et celui qui obtient un clone du "std::shared_ptr" devient un copropriétaire de l'objet pointé par le "std::shared_ptr" initial (et aussi par le clone, aussi bien que par les clones du clone, etc...). Donc l'objet créé est toujours valide tant qu'il est accessible via un "std::shared_ptr" (clone ou pas).
    Ok, je pense avoir bien assimilé cette partie là


    OK, on parle bien de champs non statiques de la classe 1 ?
    Oui, je ne travaille pour le moment pas avec du static. M'a-t-on dit que c'était dangereux de l'utiliser à tout azimuts et, bien que je comprenne le fonctionnement global, je ne pense pas encore saisir son intérêt assez concrètement pour comprendre quand l'utiliser ou pas.


    Pas besoin, vous pouvez utiliser l'original, il s'use pas quand on s'en sert.
    Ca m'embêterait qu'ils commencent à disparaître aléatoirement ! J'ai déjà assez de mal comme ça !


    Quand vous passez un shared_ptr en paramètre, vous passez un clone. Quand vous affectez un champ de type shared_ptr depuis un autre, vous clonez l'autre, etc...
    La copropriété, c'est totomatique.
    Le constructeur paraît donc parfait pour mon cas où je souhaite y avoir accès tout au long du programme, effectivement. J'aurais un clone du premier et pourrait gérer l'objet pointé dans toute les fonctions de la classe.


    Je pense que vous êtes encore trop près des pointeurs nus dans votre manière de penser les pointeurs.
    Effectivement, je n'ai pas encore bien apprivoisé les pointeurs intelligents. Je pense cependant avoir fait un bon tour de ce qui se trouve sur internet et que le problème provient plus d'un manque de pratique et d'expérimentations auxquelles me raccrocher. Je manque malheureusement de temps pour me pencher dessus comme il le faudrait/je le voudrais.


    Vous parlez d'un champ statique ou d'un champ d'instance ?
    Si c'est un champ statique, c'est à vous de l'instancier.
    Si c'est un champ d'objet, il est initialisé à pointer sur nullptr, donc pas de problème lors de sa destruction.
    Ok. J'ai donc tout un tas de nullptr un peu partout à fixer...


    ???
    Le plus simple, ne serait-il pas de le fournir dans les paramètres du constructeur, ou de l'initialiser dans le constructeur ???
    Effectivement... Ça paraît tout bête mais je n'y avais pas pensé !

    Si j'essaye de suivre le raisonnement jusqu'ici, dans le cas d'un programme Qt où je souhaite pouvoir utiliser mon objet pointé tout au long du programme :
    Le make_shared devrait se faire dans le main afin de toujours garder l'objet "en vie"

    Mettre un shared_ptr dans le constructeur d'une classe afin de faire une copie.
    La question se pose donc de "quand la classe est-elle instanciée ?"

    Mes hypothèses :
    Soit Elle l'est de base mais ça me paraît assez irréaliste et je ne vois pas comment le constructeur irait chercher le shared_ptr initial. On aurait donc un pointeur vers nullptr mais je pense qu'on peut laisser cette hypothèse de côté.

    Soit Elle l'est grâce au qmlRegisterType qui me permet de créer un objet dans mon main.qml auquel cas je ne vois pas comment lui passer en paramètre le shared_ptr initial car je ne fais aucun appel au constructeur directement ; simplement un

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    main.cpp
    qmlRegisterType<Reader>("Reader", 1, 0, "Reader");
     
    main.qml
    Reader
            {
                id:reader
            }
    Soit il me faut l'instancier, peut-être dans le main si je souhaite récupérer mon pointeur initial. Dans ce cas, je suis perturbé par le QObject *parent = nullptr qui se trouve en paramètre dans le constructeur de mes classes. Il semblerait qu'il n'apprécie pas trop que je touche aux constructeurs

    Merci encore du temps que tu me donnes et désolé de poser autant de question mais j'aimerais vraiment comprendre et chaque réponse me pousse vers une montagne d'autres questions.
    Je remarque au final que je connait plein de choses mais que je n'en comprend pas bien le fonctionnement.

  6. #6
    Expert confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 499
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 499
    Par défaut
    Ok. J'ai donc tout un tas de nullptr un peu partout à fixer...
    Un avantage des pointeurs intelligent, c'est qu'ils s'initialisent automatiquement à nullptr sans qu'on ait à le dire et ils gèrent très bien qu'ils sont "initialisés" à nullptr.

    Le make_shared devrait se faire dans le main afin de toujours garder l'objet "en vie"
    Le but d'un shared_ptr n'est pas de conserver par devers soi un objet en vie, c'est de partager la propriété avec plusieurs objets.
    Si, dans le main, vous créez un shared_ptr et que vous le passé en paramètres des constructeurs de tous les objets qui en aurait besoin, vous n'avez plus à conserver ce shared_ptr dans le main.
    Chaque objet qui a conservé un clone du shared_ptr est copropriétaire de l'objet pointé et que le main ne conserve pas le shared_ptr "original" n'a aucun impact sur la vie de l'objet pointé.

    La question se pose donc de "quand la classe est-elle instanciée ?"
    Question étrange, car il s'agit d'une question fondamentale qu'un débutant en POO se pose mais vous ne semblez pas être un débutant.
    L'instanciation d'une classe donne un objet. Cela arrive quand on déclare une variable locale (ou globale mais c'est caca) de ce type ou quand on appelle l'opérateur "new" avec le nom de la classe avec les éventuels paramètres nécessaires au constructeur de la classe.

    Soit Elle l'est de base mais ça me paraît assez irréaliste et je ne vois pas comment le constructeur irait chercher le shared_ptr initial.
    Je comprends pas. Il suffit qu'il soit passé en argument du constructeur.

    On aurait donc un pointeur vers nullptr mais je pense qu'on peut laisser cette hypothèse de côté.
    En utilisant la liste d'initialisation, le champ de type shared_ptr<XXX> sera directement initialisé à la valeur passée en paramètre (utilisation du constructeur de copie de shared_ptr).

    Soit Elle l'est grâce au qmlRegisterType
    Oulà, on sort complètement des pointeurs intelligents et on s'approche dangereusement des mécanismes internes de Qt peu compatibles avec les pointeurs intelligents.

    Il semblerait qu'il n'apprécie pas trop que je touche aux constructeurs
    Normal, on n'est dans un contexte "Qt-Object", pas des objets standards, donc on oublie les pointeurs intelligents dans ce contexte.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Problèmes avec classes et pointeurs
    Par Anas1984 dans le forum C++
    Réponses: 2
    Dernier message: 02/11/2006, 12h49
  2. Les pointeurs intelligents
    Par MatRem dans le forum C++
    Réponses: 8
    Dernier message: 20/06/2006, 19h27
  3. pointeur intelligent??
    Par yashiro dans le forum C++
    Réponses: 3
    Dernier message: 04/04/2006, 08h08
  4. Pointeur intelligent boost : return NULL ->comment faire?
    Par choinul dans le forum Bibliothèques
    Réponses: 7
    Dernier message: 21/12/2005, 16h24
  5. Classe, pile, pointeurs et casse-tête!
    Par zazaraignée dans le forum Langage
    Réponses: 6
    Dernier message: 26/09/2005, 16h57

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