Partage
  • Partager sur Facebook
  • Partager sur Twitter

Operateur ==, coment accéder aux champs privée

    24 juillet 2021 à 21:59:17

    Bonjour,

    J'ai une classe "Image" et j'ai besoin de faire sno operateur ==, or je n'arrive pas à trouver le moyen d'accéder au champs privés depuis la fonction (externe à la classe)...Je crois bien que je dois rendre l'operateur "ami" de la classe "Image", OR je ne sais absolument pas comment faire et j'ai toujours réussis à m'en passer. Bref voici en "gros" ce que j'ai pour l'instant : 

    Image.cpp (fragment)

    bool operator==(Image const& a, Image const& b)
    {
    	return (a.pixels == b.pixels);
    }
    

    Image.h

    class Image : public sf::Drawable
    {
    	unsigned char* pixels = new unsigned char[784]{0};
    
    
    public:
    
    	Image(unsigned char pixels_[784]);
    	virtual void draw(sf::RenderTarget &target,sf::RenderStates state)const;
    };
    bool operator==(Image const& a, Image const& b);
    

    Voilà voilà, merci beaucoup d'avance si vous pouvez m'aider ^^'



     

    • Partager sur Facebook
    • Partager sur Twitter
      24 juillet 2021 à 22:48:45

      Rends ton opérateur ami et ça sera "parfait".

      Cependant, la comparaison tu la fait sur des pointeurs, ce n'est certainement pas ce que tu veux. Utilise plutot des vecteurs (ou des std::array) (comme ça en plus cela éliminera toute possibilité de fuite de mémoire)

      Mais... cela a-t-il bien du sens de comparer des images si petite?

      • Partager sur Facebook
      • Partager sur Twitter
      C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
        24 juillet 2021 à 22:53:50

        Oui j'essais de faire un réseau de neuronnes et j'utilise une base de donnée d'image de pixel allant de 0 à 255. Mais dans le reste du code je crois que j'ai raté la lecture des images et certaines sont identiques. Je penserais à mettre des std::array. 

        Cependant je ne comprend pas comment la rendre amie de ma classe "Image", comment dois-je m'y prendre ^^'

        • Partager sur Facebook
        • Partager sur Twitter
          24 juillet 2021 à 23:10:29

          Salut,

          C'est tout simple: tu déclare, effectivement l'opérateur comme étant ami ... à l'intérieur de la classe :

          /* je laisse les autres erreurs, j'en parle juste après */
          class Image : public sf::Drawable
          {
              unsigned char* pixels = new unsigned char[784]{0};
           
              friend bool operator ==(Image const &, Image const &);
          public:
           
              Image(unsigned char pixels_[784]);
              virtual void draw(sf::RenderTarget &target,sf::RenderStates state)const;
          };
          bool operator==(Image const& a, Image const& b);

          Ceci étant dit, comme l'a indiqué mon commentaire, il y a plusieurs erreurs dans ce code.

          La toute première étant que, comme ta classe Image fait partie d'une hiérarchie de classes (elle hérite de la classe sf::drawable), elle a ce que l'on appelle une sémantique d'entité (l'opérateur == n'ayant réellement de sens que pour les classes ayant ce que l'on appelle une sémantique de valeur ).

          Et le fait est que, lorsqu'une classe a sémantique d'entité, il n'est pas vraiment intéressant de pouvoir comparer une instance de cette classe à une autre, pour la simple et bonne raison que chaque instance est -- par définition -- strictement unique.

          Cette unicité est d'ailleurs garantie par le fait, entre autres, que la classe sf::drawable n'est ni copiable ni assignable.  Des code comme

          Image img1{"monFichier.jpg"};
          
          Image img2{img1};

          ou comme

          Image img1{"monFichier.jpg"};
          Image img2{"autreFichier.jpg"};
          
          
          img2 = img1;

          étant carrément refusés à la compilation.

          Cela n'empêche évidemment pas de comparer les différents états (ou les données qui caractérisent ces états) exposés par ta classe Image (ex: quelle est la couleur prédominante dans img1? est-ce la même couleur que la couleur prédominante de img2?  Img3 et Img5 ont elles chargé le même nom de fichier? Le nombre de pixels de img4 et celui de de img6 est il identique? ...), mais, pour faire simple, si tu veux savoir si tu as bien obtenu l'image (très spécifique) que tu t'attendais à manipuler, il "suffit" de vérifier l'adresse mémoire de l'image que tu as obtenue et de ton image "de contrôle".

          Et c'est sans doute le genre de chose que tu ne voudra sans doute jamais faire simplement parce que, si c'est pour -- quand même -- utiliser une image très particulière que tu fournis de manière "arbitraire" -- mais qui est du coup quand même disponible -- à quoi bon demander au développeur d'en fournir une en paramètre, au risque qu'il ne te fournisse pas la bonne?

          Enfin, bref, j'espère avec ce qui précède t'avoir fait prendre conscience que non seulement l'opérateur == est complètement inutile, mais qu'il n'a, conceptuellement parlant, absolument aucun sens ;).

          Par contre, ton code, tel qu'il est présenté (une fois que l'on aura supprimé l'opérateur == dont je viens de t'expliquer l'inutilité) va présenter un énorme problème à cause de sa donnée membre pixels.

          Déjà, pourquoi voudrais tu limiter le nombres de pixels à 784, ni plus, ni moins? Je peux comprendre que cela te permet de représenter des carrés de 28 pixels de cotés (et encore, j'ai du mal à  comprendre la logique qui se cache derrière ce nombre), mais je trouve cela un peu "arbitraire": que va-t-il se passer le jour où tu voudras une image de ... 32 ou de 56 pixels de cotés?

          Mais, surtout, ta classe, telle qu'elle est là, va présenter une fuite mémoire à chaque fois que tu voudras détruire une image devenue "inutile", et ca, ca va -- à terme -- très certainement poser d'énormes problèmes à ton programme, qui deviendra -- au mieux -- au mieux "de plus en plus lent" et qui  -- au pire -- finira par planter, voir par faire planter la machine sur laquelle il s'exécute.

          Et ca, c'est vraiment la plus mauvaise des idées que l'on puisse trouver parmi les mauvaises idées à avoir, tu ne crois pas?

          Ah, ben, oui... Il faut te rappeler que C++ n'est pas comme Java ou comme C#: il n'y a pas un "ramasse miettes" (un garbage collector) pour t'aider à récupérer toute cette mémoire qui ne sert plus à rien.  Lorsque tu as recours à l'allocation dynamique de la mémoire (faisons simple: lorsque  tu utilise new ou new[]), tu t'engage personnellement auprès du système d'exploitation à lui rendre personnellement la mémoire qu'il t'a si gentiment prêtée quand tu n'en a plus besoin.

          Et, le système d'exploitation va te croire sur parole en estimant que, tant que ton application est active, si elle ne lui rend pas de la mémoire qu'elle a demandée, c'est que  la mémoire en question est encore utilisée.

          Et donc, ou bien il va falloir mettre en place un système permettant de s'assurer que la mémoire allouée à cette donnée pixels soit bien rendue au plus tard juste avant que l'on ne perde définitivement l'adresse mémoire à laquelle cet espace mémoire commence.

          Alors, tu peux bien sur la jouer "caïd" et gérer tout cela manuellement (en utilisant new dans le constructeur, ce qui implique de libérer toi-même la mémoire par appel à  delete dans le destructeur), mais l'idéal reste quand même, afin de n'avoir pas ** trop ** à t'inquiéter de tous ces problèmes d'intendance, d'utiliser une classe qui a spécialement été conçue pour prendre ces problèmes en charge: la classe std::vector.

          • Partager sur Facebook
          • Partager sur Twitter
          Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
            24 juillet 2021 à 23:43:47

            Et bien le code que je fais est HYPER particulier, dans le sens ou je le fait pas pour être portable (D'ailleurs j'ai ommis pas mal d'encapsulation parce que c'était plus simple puis parce que j'avais vraiment la flemme de faire du code propre). C'est surtout un GROS terrain de jeux, j'essais de coder un réseaux de neuronnes "à la main", dans le sens ou je m'occupe de 100% de la logique derriere et que je pourrais avoir un controle total dessus. 

            La taille des images correspond à celles offerte par une banque de donnée (ici) Et que c'est les images utilisé, mon réseau de neuronnes étant indépendant du système d'image et qu'il fallait juste un système permettant de stocker 60000 images. 

            De plus a aucun moment je supprime les images de la memoire parce que j'ai encore du mal à gérer mon csv (abominable à utiliser j'en ai ras-le-bol ^^'). 

            Mais oui dans les grandes lignes je veux surtout avoir quelque chose de fonctionnel ! La partie image n'est que temporaire et spécifique à ma base de données, quoi qu'il en soit merci de ta réponse ^^.

            EDIT : Ho ! j'ai omis la raison du "==", je suis à 100% d'accord sur le fait que chaque entité doit être unique, mais c'est juste pour vérifier si 2 de mes images sont identiques car au chargement des images ils semblerais que celle-ci soit toutes les mêmes...Alors je voulais mettre un truc un minimum porpre pour essayer ^^

            -
            Edité par DeveCout 24 juillet 2021 à 23:58:34

            • Partager sur Facebook
            • Partager sur Twitter
              25 juillet 2021 à 0:07:58

              Salut,

              J'arrive peut être un peu tard, mais pourquoi ton opérateur == est externe à ta classe ?

              class Image
              {
              private:
                 unsigned char* pixels;
                    
              public:
                 bool operator==(const Image& autre) const;
              }
              
              bool Image::operator==(const Image& autre) const
              {
                 return pixels==autre.pixels;
              }

              (la, ça résout le soucis du private, cependant, je ne suis pas sur que comparer 2 pointeurs soit ce que tu veux)

              • Partager sur Facebook
              • Partager sur Twitter

              Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

                25 juillet 2021 à 0:17:17

                Et bien je veux vérifier si les tableaux sont les mêmes...Dans leurs contenus, mais là je comprend plus rien à mon code, enfin à ce qu'il fait surtout ca devient IM-BU-VABLE j'ai envie de me tirer une balle. Disons que en gros j'ai un fichier csv contenant 60.000 lignes qui elles contienne 784 pixel chacune (sous forme de 255 nuances de gris). Mais lorsque je le charge j'ai des images (Noir et blanc de 784 pixels) qui sont les mêmes...Mais je voulais faire un test ,pour en être sur. Sauf que mon code ne marche pas...les images sont les mêmes. Enfin bbrreeff ce n'est même plus le sujet ^^', mais j'ai utliser ceci : 

                class Image : public sf::Drawable
                {
                	unsigned char* pixels = new unsigned char[784]{0};
                
                
                public:
                	friend bool operator==(Image const& a, Image const& b);
                
                	Image(unsigned char pixels_[784]);
                	virtual void draw(sf::RenderTarget &target,sf::RenderStates state)const;
                };
                

                Et ceci (.cpp): 

                bool operator==(Image const& a, Image const& b)
                {
                	return (a.pixels == b.pixels);
                }
                




                • Partager sur Facebook
                • Partager sur Twitter
                  25 juillet 2021 à 0:30:34

                  Tu as ignoré mon dernier message, dommage.

                  Mais quoiqu'il en soit comme je disais, comparer un pointeur n'est pas bon. 

                  Si tu veux savoirs si une image est égale à une autre (mêmes pixels), et que tu sais que ça fait 784 octets, utilise par exemple memcmp sur tes pointeurs.

                  • Partager sur Facebook
                  • Partager sur Twitter

                  Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

                    25 juillet 2021 à 4:10:25

                    > mais pourquoi ton opérateur == est externe à ta classe ?

                    La forme libre et amie est globalement la meilleure.

                    Mais oui (pour la 3e fois?), ce ne sont pas des pointeurs qui doivent être comparés. `pixel`devrait être un std::vector, ou un std::array si la taille est vraiment toujours la même et connue à la compilation -- ce qui manquerait de flexibilité.

                    Sinon, il faut vraiment séparer certains rôles comme le comment une image est définie (tableau de n pixels) de certains autres aspect comme son affichage. C'est un mauvais couplage. OSEF complètement qu'une image soit drawable quand on entraine un RdNA.

                    • Partager sur Facebook
                    • Partager sur Twitter
                    C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                      25 juillet 2021 à 11:06:48

                      lmghs a écrit:

                      > mais pourquoi ton opérateur == est externe à ta classe ?

                      Tu as une documentation la dessus ? Il y a des optimisations ? De meilleures performances ? 

                      Je trouve ça assez contre intuitif, puisqu'on a besoin d'aller taper dans les champs private. Je vois plutôt le "friend" comme un vilain passe doit. Un peu comme un mec en boîte de nuit qui dit "vas y, laisse le rentrer c'est mon pote, vas y, vas y ..." :D


                      Car autant minimiser le nombre de méthodes, et utiliser des fonctions qui vont appeler les quelques méthodes "clés", ok. 

                      Autant sortir une fonction (pour minimiser le nombre de méthodes ?) tout ça pour aller la mettre friend, je trouve ça assez sale.

                      • Partager sur Facebook
                      • Partager sur Twitter

                      Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

                        25 juillet 2021 à 11:59:58

                        La raison pour laquelle un opérateur == est préférable tient en gros à la commutativité de l'opération et aux éventuelles conversions implicites qui pourraient survenir.

                        Car si A ==  B, B doit pouvoir être égal à A.

                        Or, si tu travaille avec un opérateur membre, A ne peut dans tous les cas être qu'une instance de Image (dans le cas présent) mais B peut être le résultat d'une conversion implicite (par exemple, parce que l'on aurait transmis un char[784] ).

                        Mais, du coup, si B est un char[784], il faudrait créer explicitement une instance de Image à partir de B pour pouvoir faire appel à l'opérateur et assurer la commutativité de l'opération (vu qu'il n'est possible d'appeler cette fonction qu'à partir d'une instance de Image) .

                        Le seul moyen de garantir à tous les coups la commutativité de l'opération est donc de s'assurer que, si conversion implicite il doit y avoir, elle puisse survenir sur n'importe lequel des deux opérandes de l'opérateur.

                        Et, pour que cela puisse se faire, il n'y a pas d'autre choix que d'utiliser un opérateur libre ;)

                        Ce n'est donc pas une question de performances (après tout, une fonction membre non statique n'est jamais qu'une fonction à laquelle le compilateur aura rajouté un pointeur sur l'instance de la classe comme premier paramètre sans nous le dire).

                        C'est d'avantage lié aux exigences intrinsèques de l'opérateur == et du domaine dans lequel il évolue ;)

                        Fvirtman a écrit:

                        Je trouve ça assez contre intuitif, puisqu'on a besoin d'aller taper dans les champs private. Je vois plutôt le "friend" comme un vilain passe doit. Un peu comme un mec en boîte de nuit qui dit "vas y, laisse le rentrer c'est mon pote, vas y, vas y ..." :D

                        Tu devrais plutôt voir cela comme le collègue intérimaire occasionnel: quand le type vient dans le cadre de son interim, il est "normal" qu'il ait accès aux mêmes endroits et aux même conditions que n'importe quel employé ayant son niveau de responsabilité.

                        Car -- faut il le rappeler?  -- les opérateurs ne sont que des fonctions qui font intrinsèquement partie de l'interface de la classe que tu développe et leur existence ne tient qu'à ton souhait de fournir (ou non) la fonction équivalente.

                        Or, tu ne places les données dans l'accessibilité private que par "commodité personnelle", pour éviter que "quelqu'un" n'ayant pas forcément en tête tous les invariants liés à la classe n'aille y "foutre le bordel" et, si on y réfléchit bien, les classes susceptibles d'exposer un opérateur == ayant -- a priori -- sémantique de valeur, ce pourrait tout aussi bien être des structure POD composées exclusivement de données publiques.

                        En outre, le fait de fournir, à une fonction quelconque, un "accès privilégié" au données privées d'une classe t'évite d'exposer cet accès de manière "moins controlée", dans le sens où, autrement, tu devrais rajouter un accesseur (que n'importe qui pourrait alors décider d'utiliser, pour en faire n'importe quoi) équivalent.

                        Il ne faut bien sur pas multiplier les relations d'amitié à l'envie -- ne dit on pas que l'ami de tout le monde n'est l'ami de personne? -- mais l'amitié "contrôlée", déclarée pour une ou deux fonctions, guère plus, permet de renforcer énormément l'encapsulation dans le cas présent ;)

                        Enfin, il n'y a rien qui t'empêche de t'assurer que la fonction amie n'utilise, par exemple, que des fonctions publiques et protégées, sans aller chipoter aux données privées, ce qui la placerait plutôt au même niveau que les classes dérivées, d'un point de vue conceptuel ;)

                        • Partager sur Facebook
                        • Partager sur Twitter
                        Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                          25 juillet 2021 à 12:44:34

                          @Fvirtman

                          Une fonction amie n'a jamais qu'exactement les mêmes privilèges qu'une fonction membre. La différence, c'est la syntaxe d'appel. Dans un monde d' d'œillères OO, les amis ont eu mauvaise presse. Mais ce ne sont jamais que des fonctions de la classe qui ne sont pas membres.

                          Maintenant, pourquoi libre? Pour la symétrie comme Koala01 l'a dit. John Lakos en a aussi parlé brièvement dans sa conf sur la "Value semantics", mais j'imagine qu'il doit s'étendre plus sur la question ailleurs. Je curieux serai de voir ce qu'il a à dire sur le sujet car cela risque de dépasser le seul confort syntaxique. Ca fait un paquet de temps que l'on insiste que cette forme (libre) est à préférer.

                          Pourquoi ami? Pour en faire un ami caché, et re-accélérer les temps de compilations, mais surtout réduire (de nouveau) la quantité de messages d'erreurs avec une fonction libre: quand le compilo ne trouve pas d'opérateur == pour "poire1 == pomme2" on n'en a rien à faire qu'il existe une surcharge d'operator== pour les Images. ==> ami caché. J'en ai déjà parlé plus en détails ces derniers mois.

                          Le fait que cela donne accès au champs interne devient presque un bonus, à mon goût, qui répond au besoin exprimé de l'OP.

                          -
                          Edité par lmghs 25 juillet 2021 à 12:50:37

                          • Partager sur Facebook
                          • Partager sur Twitter
                          C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                            25 juillet 2021 à 14:17:03

                            > Tu as une documentation la dessus ? Il y a des optimisations ? De meilleures performances ?
                            Niveau responsabilités, ce n'est pas à X de définir comment il se compare à un autre X. Tout comme ce n'est pas à un carré de se charger de se comparer avec un autre carré.
                            • Partager sur Facebook
                            • Partager sur Twitter
                              26 juillet 2021 à 23:57:29

                              J'utilisais déjà friend pour des opérations de type  float* vec3d  (commutative, et donc on ne peut pas surcharger le float, donc obligé de mettre un friend dans vec3d)

                              Cependant, c'est vrai que j'aimais bien me passer de friend dans pas mal de cas. Je verrai ce que je ferai à l'avenir. Merci pour ces explications.

                              -
                              Edité par Fvirtman 26 juillet 2021 à 23:59:41

                              • Partager sur Facebook
                              • Partager sur Twitter

                              Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

                              Operateur ==, coment accéder aux champs privée

                              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                              • Editeur
                              • Markdown