Partage
  • Partager sur Facebook
  • Partager sur Twitter

Polymorphisme, shared_ptr, dépendances circulaires

Sujet résolu
    30 mars 2019 à 10:57:24

    Bonjour,

    Voilà, ça fait plusieurs jours que je suis sur un problème que j'arrive vraiment pas à résoudre. 

    J'ai une erreur lorsque j'essaie de passer un shared_ptr vers un objet A dans une méthode qui prend un shared_ptr vers un objet B. (A hérite de B). L'erreur :

    cannot convert argument 1 from 'std::shared_ptr<A>' to 'std::shared_ptr<B>'

    1. Quelles peuvent être les causes de cet erreur ?

    2. J'ai aussi des problèmes de dépendances circulaires dans mon code (que j'ai réussi à résoudre en tâtonnant), est-ce qu'une classe qui n'est pas correctement définie/incluse pourrait être à l'origine de cette erreur ?

    3. Tant qu'on y est, connaissez-vous un moyen d'éviter les dépendances circulaires dans un code ? (un pattern, peut-être ?).

    • Partager sur Facebook
    • Partager sur Twitter
      30 mars 2019 à 11:08:54

      1. A hérite de B. En revanche, std::shared_ptr<A> n'hérite pas de std::shared_ptr<B>. std::dynamic_pointer_cast va pouvoir t'aider.

      2. Une dépendance cyclique peut être évitée à l'aide de forward declaration. Si une classe B n'utilise que des pointeurs de A (ou références), alors tu peux faire une forward declaration de A dans le header B.

      3. cf. 2. Sinon, il s'agit peut-être d'un problème de conception.

      -
      Edité par anolya 30 mars 2019 à 11:10:17

      • Partager sur Facebook
      • Partager sur Twitter
        30 mars 2019 à 11:27:06

        1. Justement, le truc qui est bizarre c'est que j'ai le même cas à un autre endroit de mon code, mais ça n'a jamais posé problème et il fait la conversion tout seul.

        2. Effectivement, mais ça devient très vite compliqué et pratiquement impossible à résoudre quand on a beaucoup de classes qui s'entre-utilisent.

        3. "Un problème de conception", du coup, existe-t-il de bonnes pratiques pour éviter ces problèmes ?

        Ah et pis j'en profite pour poser une autre question :

        4. Dans le cas ou j'ai besoin de pointeurs et que je n'utilise pas d'allocation dynamique de mémoire (pattern Observer par exemple), reste-t-il vraiment un avantage à utiliser des pointeurs intelligents ?

        • Partager sur Facebook
        • Partager sur Twitter
          30 mars 2019 à 12:14:46

          4. Tout dépend de l'encapsulation des données. Un smart pointer est fait pour respecter le RAII, à savoir que dès qu'il y a une ressource occupée, on s'assure de la libération future certaine de cette ressource. Ainsi, on ayant un objet simple (std::shared_ptr par exemple) que l'on se transmet par copie de proche en proche, on s'assure que la ressource sera disponible aussi longtemps que quelqu'un l'utilisera et sera détruite à l'instant même où l'on sortira du scope de chacun de ces objets, par exemple sur exception, signal, sortie de fonction, suppression d'objet,... Il vaut toujours mieux privilégier une référence à un pointeur, intelligent ou non.

          1. A mon avis, c'était un coup de malchance. Je te conseille de toujours utiliser les fonctions permettant de passer de l'un à l'autre pour éviter tout problème.

          2-3. 1 classe = 1 fonctionnalité ; 1 fonction = 1 chose à faire. Si autant de classes sont interdépendantes, je pense que tu cherches à faire compliqué. C'est compliqué d'en être certaine sans voir le code. Prenons la STL par exemple :

          • std::basic_string manipule les chaines de caractères.
          • std::vector manipule les tableaux de taille dynamique.
          • std::array manipule les tableaux de taille statique.
          • std::thread permet de créer des threads (parfois appelés processus légers)
          • std::chrono:time_point s'occupe d'avoir des références dans le temps
          • ...
          • std::copy copie les éléments d'un conteneur à l'autre
          • std::map::find trouve un élément grâce à la clé que tu lui transmets
          • ...

          Demande-toi aussi si, de manière conceptuelle, c'est à l'entité A de modifier des trucs ou si l'entité A se contente de faire une action qui va modifier un environnement. (par exemple, dans un RPG, un attaque donne un coup, l'environnement affecte le coup à l'objet qu'il faut)

          -
          Edité par anolya 30 mars 2019 à 12:16:10

          • Partager sur Facebook
          • Partager sur Twitter
            30 mars 2019 à 12:32:04

            Je vais essayer le dynamic cast et surtout revoir l'architecture de la partie de mon code qui pose problème. C'est vrai que le SRP m'aidera sûrement à réduire le coupling.

            Merci de tes réponses et bon week-end !

            -
            Edité par Raynobrak 30 mars 2019 à 12:34:03

            • Partager sur Facebook
            • Partager sur Twitter
              30 mars 2019 à 12:40:30

              Si c'est pour stocker dans un conteneur de Base, stocke des PI sur base, je me suis fais cette petite fonction template pour faire des unique_ptr:

              template<class B,class D,class... Args>

              std::unique_ptr<B> make_unique_base(Args &&... args)

              {

                  static_assert(std::is_base_of<B,D>::value,"B must be a base of D");

                  return std::unique_ptr<B>(new D(std::forward<Args>(args)...));

              }

              Elle est très facilement adaptable pour produire des std::shared_ptr.

              PS Le menu déconne complètement ce midi, impossible d'utiliser les balises ;(

              -
              Edité par int21h 30 mars 2019 à 12:42:53

              • Partager sur Facebook
              • Partager sur Twitter
              Mettre à jour le MinGW Gcc sur Code::Blocks. Du code qui n'existe pas ne contient pas de bug

              Polymorphisme, shared_ptr, dépendances circulaires

              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
              • Editeur
              • Markdown