Partage
  • Partager sur Facebook
  • Partager sur Twitter

MAP

operator <<

Sujet résolu
    8 janvier 2019 à 19:44:37

    Bonjour tt le monde je fait une classe Graphe en utilisant des map :

    template <class Sommet, class Poids> class Graphe {
    
    
    private:
        bool orientation;
        map<Sommet,vector<pair<Sommet,Poids> > > graphe;
    public:
        ...
    }

    et je voudrais redefinir l'operator << pour laffichage et je sais que il faut faire une methode amical sinon je n'aurais pas l'acces au attribut de ma classe c'est que je suis nouveau en c++ et je narrive pas l'entete de la foncton !

    merci d'avance pour tt les aides

    • Partager sur Facebook
    • Partager sur Twitter
      9 janvier 2019 à 10:14:42

      Que veux-tu afficher exactement ? Car je ne pense pas que tu sois obligé d'utiliser l'amitié...

      • Partager sur Facebook
      • Partager sur Twitter
        9 janvier 2019 à 11:35:02

        Heu,

        Tu peux nous expliquer exactement ce que tu as en tête ?

        Ta classe Graph me parais bien compliquée.

        • Partager sur Facebook
        • Partager sur Twitter
          9 janvier 2019 à 11:36:00

          Pour déclarer un opérateur amical, tu peux faire :

          template<class Sommet, class Poids>
          friend std::ostream& operator<<( std::ostream&, Graphe<Sommet,Poids> const& );

          Il est surement un peu "trop" amical car Tous les operator<< quels que soient les types Sommet et Poids seront amis de tous les Graphe quels que soient les types Sommet et Poids.
          Définir que l'ami de Graphe<Sommet,Poids> est l'opérateur qui utilise exactement les même Sommet et Poids est nettement plus lourd à écrire.

          • Partager sur Facebook
          • Partager sur Twitter

          En recherche d'emploi.

            9 janvier 2019 à 15:33:28

            Salut,

            Il y a une règle simple à suivre en programmation, pour se faciliter la vie:

            Chaque nom (ou groupe nominal) de l'analyse des besoins doit être représenté dans le code par une donnée ou par un type de donnée

            chaque verbe (ou groupe verbal) de l'analyse des besoins doit être représenté dans le code par une fonction

            Dans le cas présent, ton analyse des besoins aura sans doute fait apparaître au moins trois noms (ou groupe nominaux):

            • la notion de graphe
            • la notion de sommet (ou de point)
            • la notion de "bord", de "liaison" ("edge" en anglais).

            La notion de point pourra être représentée de manière assez simple sous la forme d'un point 2D (ou 3D selon le cas) proche de

            struct Point{
                Point(int x, int y):x{x},y{y}{
                }
                int x;
                int y;
            };

            et, une fois que cette notion existe, nous pourrons utiliser l'opérateur << pour en provoquer l'affichage sous une forme proche de

            std::ostream & operator <<(std::ostream & ofs, Point const & point){
                ofs<<"x : "<<point.x<<" y : "<<y;
            }

            En outre, comme il semble utile de pouvoir trier les différents poins, nous allons définir les opérateur de comparaison< et == pour cette notion sous une forme qui pourrait ressembler à

            bool operator == (Point const & a, Point const & b){
               return a.x == b.x && a.y==b.y;
            }
            bool operator <(Point const & a, Point const & b){
                auto tiea = std::tie(a.x, a.y);
                auto tieb = std::tie(b.x, b.y);
                return tiea <tieb; 
            }

            La notion de bord est très importante ici, car c'est celle qui permet -- justement -- de faire la liaison entre deux sommet de ton graphe. Il faut donc commencer par donner corps à cette notion, par exemple, sous une forme simple ressemblant à

            struct Edge{
                Edge(Point const & p, Weight w):
                    destination{p}, weight{w}{
                }
                Point const & destination;
                Weight weight;
            };

            Une fois que cela sera fait, nous pourrons sans problème définir l'opérateur << pour en provoquer l'affichage en cas de besoin, par exemple, sous une forme proche de

            std::ostream & operator<<(std::ostream & ofs, Edge const & edge ){
                ofs<<"    Destination : "<<edge.destination<<"\n"
                   <<"    Poids       : "<<edge.weight;
                return ofs;
            }

            Mais nous nous rendrons compte qu'il y a une troisiième notion qui vient "s’intercaler' entre la notion de graphe et la notion de liaison : la notion de liste de liaisons, parce que chaque sommet de ton graphe semble pouvoir être relié à plusieurs sommets différents, voire, disposer de plusieurs liaisons de poids différents vers un sommet identique.

            Nous devons donc faire apparaître cette notions dans le code, sans doute sous une forme qui expose une interface simplifiée de collection qui serait proche de

            class EdgeList{
                using vector_t = std::vector<Edge>;
            public:
                /* pour la facilité */
                using iterator = typename vector_t::iterator;
                using const_iterator = typename vector_t::const_iterator;
                /* pour pouvoir chainer les appels, nous renvoyons la liste des liaisons
                 * après ajout d'une lisaison
                EdgeList & push(Sommet sommet, Weight weight){
                    edges_.push_back({sommet, weight});
                    return *this;
                }
                iterator begin() {
                    return edges_.begin();
                }
                iterator end(){
                    return edges_.end();
                }
                const_iterator begin() const{
                    return edges_.begin();
                }
                const_iterator end() const{
                    return edges_.end();
                }
                size_t size() const{
                    return edges_size();
                }
                Edge & operator[](size_t index) {
                    assert(index < size() && "Index out of bound");
                    return edges_[index];
                }
                Edge const & operator[](size_t index) const{
                    assert(index < size() && "Index out of bound");
                    return edges_[index];
                }
                void clear() {
                    edges_.clear();
                }
            private:
                vector_t edges_;
            };

            Une fois cette notion présente, il serait simple de provoquer l'affichage de toutes les liaisons partant d'un point donné, en définissant l'opérateur << (tu remarqueras que la fonction n'a aucun besoin d'être amie, vu que nos disposons de "tout ce qui est nécessaire" dans l'accessibilité publique) sous une forme proche de

            std::ostream & operator<<(std::ostream & ofs, EdgeList const & list){
                std::cout<<"Nombre de liaisons : "<<list.size()<<"\n";
                size_t index{0};
                for(auto const & it : list){
                    ofs<<"lisaison "<< index<<" : "<<it<<"\n";
                    ++index
                }
                return ofs;
            }

            Maintenant que nous avons les deux notions de base, il devient "facile" de créer la notion de graphe :  car c'est -- pour faire simple -- une liste de points (de sommets), associée à une "liste de liaisons" pour chacun des points.

            Nous allons donc donner à notre graphe une interface proche de celle de n'importe quelle collection, mais, en plus, nous allons nous donner l'occasion de récupérer la liste des liaisons, ce qui nous donnera quelque chose ressemblant à

            class Graph{
            public:
                EdgeList & addPoint(int x, int y){
                    points_.push_back(Point{x,y});
                    return edgee_.insert(Point{x,y}).second;
                }
                size_t size() const{
                    return points_.size();
                }
                using const_iterator = std::vector<Point>::iterator;
                const_iterator begin() const{
                    return points_.begin();
                }
                const_iterator end() const{
                    return points_.end();
                }
                Point const & operator[](size_t index) const{
                    assert(index < size() && "Index out of bound");
                    return points_[index];
                }
                void clear(){
                    points_.clear();
                    edges_.clear();
                }
                EdgeList & edges(Point const & p){
                    auto it = edges_.find(p);
                    assert(it!= edges_.end() && "Point not found");
                    return it.second;
                }
                EdgeList const & edges(Point const & p) const{
                    auto it = edges_.find(p);
                    assert(it!= edges_.end() && "Point not found");
                    return it.second;
                }
                bool exists(Point const & p){
                    return edges_.find(p)!=edges_.end();
                }
            private:
                std::vector<Point> points_;
                std::map<Point, EdgeList> edges_;
            }

            Et, encore une fois, il devient très facile d'afficher toutes les informations auxquelles notre graphe nous donne accès sous une forme qui serait sans doute proche de

            std::ostream & operator<<(std::ostream & ofs, Graph const & g){
                for(auto const & it : g){
                    ofs<<"Point d'origine : "<<it<<"\n";
                    auto const & edges = g.edges(it);
                    std::cout<< edges.size()<<" lisaisons\n"
                             <<edges<<"\n";
                }
            }

            Et voilà...  En respectant la règle simple que j'ai mise en avant au début de cette intervention, j'en suis arrivé à ne pas avoir une seule fonction de plus de huit lignes, et j'ai su garder la logique extrêmement simple ;)

            NOTA: il se peut -- bien sur -- que tu aies quelques adaptations à faire, car je n'ai aucune idée du genre de graphe que tu veux créer.  Mais tout est là, prêt à l'emploi ;)

            • 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
              18 janvier 2019 à 11:08:45

              merci beaucoup les amis j'ai eu ma reponse
              • Partager sur Facebook
              • Partager sur Twitter

              MAP

              × 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