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

 C++ Discussion :

ajout d'un élément à une liste (structure)


Sujet :

C++

  1. #1
    Nouveau membre du Club
    Inscrit en
    Mai 2009
    Messages
    56
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 56
    Points : 29
    Points
    29
    Par défaut ajout d'un élément à une liste (structure)
    J'ai essayé de manipuler une liste en y ajoutant un élément à l'aide d'une méthode .
    La définition de la liste est :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    struct Chemin{
    	Case* laCase;
    	Chemin* suivant;
    	};
    Et le code de la méthode d'ajout est:
    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
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    void Itineraire::add(Case& B)
    {
     
     
    	Chemin *nouveau_item=new Chemin;
    	nouveau_item->laCase=new Case(0,0);
    	nouveau_item->suivant=NULL;
     
     
    	nouveau_item->laCase->set_Ligne(B.get_Ligne());
    	nouveau_item->laCase->set_Colonne(B.get_Col());
    	nouveau_item->suivant=NULL;
     
     
     
    if ( chemin==NULL )
    	chemin=nouveau_item;
    else
    {
    	Chemin* auxiliaire=new Chemin;
     
    	/*auxiliaire->laCase=chemin->laCase;
    	auxiliaire->suivant=chemin->suivant;*/
     
    		for(auxiliaire=chemin; auxiliaire->suivant!=NULL;){//atteindre la fin de la liste
    			auxiliaire=auxiliaire->suivant;}
    	auxiliaire->suivant=nouveau_item;
    	}
     
     
     
     
    }
    Mon problème est que la variable chemin ne prend plus les nouveaux éléments!

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 617
    Points : 30 636
    Points
    30 636
    Par défaut
    Salut,

    Sais tu que, en C++, une structure ne présente que très peu de différences par rapport à une classe, et que, par conséquent, tu peux parfaitement envisager de prévoir un constructeur qui te permettrait de t'éviter bien du boulot

    Je me demande d'ailleurs pourquoi tu passe par un constructeur qui pourrait être considéré comme... un constructeur par défaut (définissant ligne et colonne à 0) pour, tout de suite après, appeler un mutateur (qui, selon moi, ne devrait pas exister) sur ces deux valeurs

    En effet, une fois qu'une case a pris une position (en ligne et en colonne), on peut estimer... qu'elle la gardera, et qu'il n'est donc pas opportun d'en permettre le déplacement

    De plus, à moins que ta classe / structure Case ne nous cache quelque chose, *peut-être* serait il intéressant de créer ta structure Chemin (qui aurait d'ailleurs avantage à être renommée, car elle ne représente pas un chemin, mais plutôt une étape du chemin ) de telle manière à ce qu'elle contienne une instance de Case et non un pointeur vers une instance de Case, et donc, d'utiliser le constructeur par copie de l'objet Case (ou, à défaut, d'appeler directement le constructeur adéquat

    Enfin, j'ai l'impression que ta structure Chemin est en fait destinée à travailler comme un itérateur, et que, comme tout itérateur, ce que l'on peut lui demander, c'est:
    • de fournir l'accès à l'itérateur suivant
    • de permettre d'accéder à l'objet qu'il représente, en lui donnant, par exemple, une sémantique de pointeur.

    Au final, ta structure pourrait donc devenir une classe proche de (je la renomme )
    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
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    class Etape
    {
        /* cette amitié nous permet de ne pas exposer plus que nécessaire
         * l'existance du pointeur m_next
         */
        friend class Chemin;
        public:
            Etape * operator++(){return m_next;}
            /* les opérateurs nous permettant de manipuler l'itérateur comme
             * s'il s'agissait d'un pointeur
             */
           Case & operator *(){return m_case;}
           Case const & operator *() const{return m_case;}
           Case * operator ->(){return & (operator *())};
           case const * operator ->() const {return & (operator*());}
        private:
            /* les constructeurs et le destructeur sont déclarés private, de manière
             * à n'en autoriser l'accès qu'à la classe Chemin
             */
            /* un constructeur appelant le constructeur de Case */
            Etape(int ligne, int colonne):m_case(ligne,colonne),m_next(NULL){}
            /* un autre se basant sur le constructeur par copie de Case */
            Etape(Case const & c):m_case(c),m_next(NULL){}
            /* le constructeur par copie et l'opérateur d'affectation sont déclarés
             * private et non définis (en attendant C++0x)
             */
            Etape(Etape const &);
            Etape& operator=(Etape const &);
            /* Ce qui nous permet de passer à l'itérateur suivant
             * (intervenant dans la sémantique de pointeur que l'on donne à notre
             * classe)
             */
            Case m_case;
            Etape* m_next;
    };
    Enfin, ce n'est qu'une habitude, mais, j'aime faire en sorte que mes listes fournissent l'ajout d'éléments en temps constant (pour qu'elle présente quand même un avantage par rapport aux tableaux dynamiques ), alors que, tel que tu présente le code, le temps mis à l'ajout d'un élément dépendra... du nombre d'éléments que ta liste contient déjà...

    Pour m'y aider, j'ai pris l'habitude de maintenir deux pointeur dans ma liste: le premier représentant le premier élément, et l'autre représentant le dernier.

    En ayant modifié la structure comme indiqué, la classe Chemin deviendrait proche de
    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
    20
    21
    22
    23
    24
    25
    26
    class Chemin
    {
        public:
            Chemin():m_first(NULL),m_last(NULL){}
            ~Chemin()
            {
                while(m_first)
                {
                    Etape* temp=m_first->m_next;
                    delete m_first;
                    m_first=temp;
                }
            }
            void add(Case const & c)
            {
                Etape * temp=new Etape(c);
                if(!m_first)
                    m_first=temp;
                if(m_last)
                    m_last->m_next=temp;
                m_last=temp;
            }
        private:
            Etape* m_first;
            Etape* m_last;
    };
    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
    Nouveau membre du Club
    Inscrit en
    Mai 2009
    Messages
    56
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 56
    Points : 29
    Points
    29
    Par défaut
    Vraiment,c'est génial..oui surtout d'avoir un qui explique les choses ainsi..
    Merci bien Koala!!!
    Mais si on a voulu(imposer) bien avoir "Chemin" comme structure?alors comme ça les choses deviennent plus difficiles?!

  4. #4
    Nouveau membre du Club
    Inscrit en
    Mai 2009
    Messages
    56
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 56
    Points : 29
    Points
    29
    Par défaut
    Salut,
    il me reste un petit problème dû au fait que je n'est pas compris les redéfinitions de la classe "Etape"(Chemin pour moi) .En fait j'ai voulu afficher les éléments d'itinéraire ,comme suit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    void Itineraire::afficher(){
    	int v,d;
     
     
     
    	Chemin*	auxiliaire=depart;
    	cout<<"\n\n\n"<< auxiliaire->m_case.get_Ligne()<<"\n\n\n"<<endl;
    	 while(auxiliaire!=arrivee)
                {v=auxiliaire->get_case().get_Col();d=auxiliaire->get_case().get_Ligne();
    				cout<<("(")<<v<<(",")<<d<<("   ");
    				auxiliaire=auxiliaire->m_next;
    				}
    }
    or je me surpris que les valeurs des cases de départ changent dès la première utilisation de get_Ligne()!
    en notant bien que tu l'as utilisé dans ton code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      Chemin* temp=new Chemin(c);cout<<"\n\n\n"<<temp->m_case.get_Ligne()<<"\n\n\n"<<endl;
    et ça a bien marché.

  5. #5
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 617
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 617
    Points : 30 636
    Points
    30 636
    Par défaut
    Citation Envoyé par dingua Voir le message
    Vraiment,c'est génial..oui surtout d'avoir un qui explique les choses ainsi..
    Merci bien Koala!!!
    Mais si on a voulu(imposer) bien avoir "Chemin" comme structure?alors comme ça les choses deviennent plus difficiles?!
    Qu'entends tu par imposer d'avoir "chemin" comme structure

    Est-ce "uniquement" le fait d'utiliser le mot clé struct et non le mot clé class, ou est-ce le fait de n'avoir qu'une structure au sens C du terme (un simple agrégat de données n'ayant pas de fonctions membres)

    Dans le premier cas, comme je l'ai expliqué, tu peux utiliser les termes class et struct de manière strictement équivalente, du moins dans le cas présent.

    La seule différence flagrante étant la visibilité par défaut des membres (publique pour les structures, privée pour les classes).

    Dans le second cas, rien ne t'empêche d'abandonner l'idée de travailler en orienté objet, et de sortir les différentes fonctions membres de ta structures.

    En ne plaçant qu'une déclaration anticipée de la structure dans le fichier d'en-tête (en plus de la seule déclaration des fonctions) et en définissant la structure et les fonctions dans un fichier d'implémentation séparé, tu peux obtenir une structure pour laquelle la seule chose que l'utilisateur en connaisse soit... les fonctions qui permettent de manipuler cette structure (un peu à l'image du FILE en C)
    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

  6. #6
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 617
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 617
    Points : 30 636
    Points
    30 636
    Par défaut
    Citation Envoyé par dingua Voir le message
    Salut,
    il me reste un petit problème dû au fait que je n'est pas compris les redéfinitions de la classe "Etape"(Chemin pour moi) .
    Je vais donc tâcher d'être plus clair.

    Je suis très attaché à la notion de responsabilité unique.

    Plus tu arrivera à créer des classes et des fonction n'ayant qu'une responsabilité unique, plus elles seront simples d'utilisation, et donc simples à implémenter (et à maintenir), et plus tu pourra envisager de les réutiliser.

    Ainsi, l'idée générale est qu'une classe, une structure ou une fonction ne doit s'occuper que d'une seule chose, mais doit... bien s'en charger.

    C'est à dire que, si tu veux créer une liste de cases appelée "chemin", il faut, en réalité, prévoir trois types bien distincts:
    • La case, qui représente la position à laquelle se trouve un objet donné
    • Le chemin, qui représente l'ensemble des positions par lesquelles un objet donné va passer pour rejoindre deux position données (que nous pouvons qualifier de "point de départ" et de "point d'arrivée") et
    • un troisième type de donnée qui permettra de relier les différentes cases entre elles pour, justement, accéder à la "case suivante" de proche en proche.

    C'est ce troisième type de données que j'ai appelé "étape"

    En effet, sémantiquement parlant, une étape présente "intuitivement" la notion "d'endroit atteint lorsque l'on a parcouru une partie de chemin", et fait donc "naturellement" le lien entre un endroit (une case) donné(e) et... le chemin qui permet de s'y rendre.

    Ensuite, il faut réfléchir aux service que l'on attend de ces différents types de données.

    Une case représente une position (sur un échiquier, par exemple). Les services que l'on attend d'elle sont: de nous donner une indication de numéro de ligne et de numéro de colonne.

    Mais les données qui nous permettent de représenter les numéros de lignes et de colonnes font partie de ce que l'on appelle "les détails d'implémentation"...

    Autrement dit, l'utilisateur du type Case ne devrait pas avoir à s'inquiéter de la manière dont c'est effectivement géré (ce pourrait être sous la forme de deux int séparés, sous celle d'un tableau de deux int, voire, pourquoi pas, sous une forme tout à fait différente )

    Une fois qu'elle est créée, la case ne se déplace pas, il n'y a donc pas lieu de permettre de changer le numéro de la ligne ou le numéro de colonne qu'elle représente.

    La responsabilité de la case est, tout simplement, de représenter une position (basée sur les lignes et les colonnes)
    Il faut donc prévoir de:
    1. pouvoir la créer (en lui donnant une valeur de ligne et une valeur de colonne)
    2. pouvoir la copier
    3. pouvoir l'assigner
    4. pouvoir la détruire
    5. pouvoir obtenir le numéro de ligne qu'elle représente
    6. pouvoir obtenir le numéro de colonne qu'elle représente

    Il faut savoir que, si on ne définit pas nous même un comportement pour [2][3] et [4] (normalement pour [1] aussi, mais avec la restriction qu'il ne peut pas prendre d'argument, et cela ne s'applique donc pas ici) le compilateur fournira "d'office" une implémentation de ces comportements qui, dans le cas présent, nous conviendra parfaitement...

    Au final, le type Case prendrait une forme proche de
    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
    20
    class Case
    {
        public:
            Case(int l, int c):m_linge(l),m_colonne(c)  // (1) (*) (**)
            {
            }
            /* laissons au compilateur le soin d'implémenter (2), (3) et (4) */
            int linge() const // (5)
            {
                return m_linge;
            }
            int  colonne() const // (6)
            {
                return m_colonne;
            }
        private:
            /* les "détails d'implémentation dont l'utilisateur n'a pas à s'inquiéter */
            int m_linge;
            int m_colonne;
    }
    (*) Les numéros entre parenthèses font référence aux numéro de la liste des services énumérées plus haut
    (**) cette syntaxe s'appelle une liste d'initialisation, qu'il est conseillé d'utiliser chaque fois que possible de préférence au fait d'assigner les valeurs des membres dans le corps même du constructeur. cf cette entrée de la FAQ pour en savoir d'avantage

    Cette structure serait sans doute avantageusement servie par quelques fonctions libres permettant de les comparer, sous une forme proche de
    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
    // pour tester l'égalité
    bool operator ==(Case const & c1, Case const & c2)
    {
        return c1.ligne()== c2.ligne() && c1.colonne()==c2.colonne();
    }
    // pour tester l'inégalité
    bool operator !=(Case const & c1, Case const & c2)
    {
       return !(c1==c2);
    }
    // pour permettre un tri par ordre de "grandeur"
    bool operator <(Case const & c1, Case const & c2)
    {
        /* j'ai considéré ici que le nombre de colonnes par ligne 
         *est toujours égal 
         */
        return c1.ligne()*c1.colonne()< c2.ligne()*c2.colonne();
    }
    Intéressons nous maintenant au type de donnée permettant de parcourir les différentes cases de proche en proche que j'ai nommé Etape.

    Sa responsabilité est de faire le lien entre une case donnée et la case "suivante" à parcourir lorsque l'on emprunte le chemin

    D'abord, les service que l'on peut attendre de lui:
    1. Il faut pouvoir le construire (mais, idéalement, seule la liste devrait pouvoir le faire) (*)
    2. Il faut pouvoir le détruire (mais, encore une fois, seule la liste devrait pouvoir le faire) (*)
    3. on ne doit pas pouvoir l'assigner (**) (***)
    4. on ne doit pas pouvoir le copier(**) (***)
    5. Il doit permettre d'obtenir la case à laquelle il fait référence
    6. Il doit permettre d'accéder à l'élément suivant

    (*) Si nous arrivons à éviter que "n'importe qui" ne tente de créer une étape, nous obtenons l'assurance que l'utilisateur ne fera pas de c...ries. Le moyen "idéal" pour y arriver passe par l'amitié (cf la page de la FAQ qui en parle), et la définition des constructeurs et destructeurs dans l'accessibilité privée
    (**) il s'agit en définitive d'une conséquence de (*)
    (***) En attendant la prochaine norme (C++0x), le meilleur moyen d'y arriver est de déclarer l'opérateur d'affectation (operator = ) et le constructeur par copie ( MaClass(MaClass const&) ) dans l'accessibilité privée et de ne pas les définir

    Les détails d'implémentation qui lui permettront de fournir ces services sont:
    • le fait qu'il dispose d'une variable de type ... case
    • le fait qu'il dispose d'un pointeur vers... l'élément suivant

    Nous pourrions donc avoir une classe Etape proche de
    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
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    class Etape
    {
        /* donne à la classe Chemin accès à tout ce que la classe 
         * Etape contient
         */
        friend class Chemin;
        public:
            /* Les fonctions qui permettent à l'utilisateur d'utiliser la 
             * classe Etape
             */
            Case const & value() const{return m_case;}
            Etape const * next() const{return m_next;}
            /* Nous pouvons considérer la classe Etape comme...
             * un pointeur sur une variable Case...
             * les opérateurs qui nous intéressent alors 
             *    sont l'étoile " * " (pour pouvoir écrire (*monEtape).colonne() )
             *    la fleche " -> "  (pour pouvoir écrire monEtape->ligne()
             *    l'opérateur d'incrémentation (pour pouvoir écrire ++monEtape )
             * mais cela reste facultatif, et a pour seul but de faciliter
             * la vie à l'utilisateur :D
             */
            Case const & operator *() const{return m_case;}
            Case const * operator->() const{return &m_case;}
            Etape * operator++() const{return m_next;}
        private:
            /* tout ce qui n'est pas accessible à l'utilisateur
             * mais qui l'est pour la classe Chemin du fait de son amitié 
             */
            /* un constructeur créant  spécialement pour l'occasion
             * une case sur base de la ligne et de
             * la colonne
             */
           Etape(int l, int c):m_case(l,c),m_next(NULL){}
           /* un autre utilsiant le constructeur par copie de Case */
           Etape(Case const & c):m_case(c),m_next(NULL){}
           /* Le destructeur ne fait rien, mais si on veut en limiter l'acces,
            * il faut le définir dans l'accessibilité private (l'amitié déclarée
            * avec la classe Chemin permettra à cette classe uniquement
            * d'y accéder
            */
           ~Etape(){}
           /* interdisons l'assignation et la copie...
            * si l'utilisateur essaye d'y faire appel, il obtiendra une erreur
            * de compilation
            *
            * si la classe amie Chemin essaye de s'en servir, il faudra attendre
            * l'édition de liens avant de s'en rendre compte, mais, c'est déjà
            * pas si mal :D
            */
           Etape & operator=(Etape const &);
           Etape(Etape const &);
           /* et enfin, les détails d'implémentation permettant à l'Etape
            * de rendre les services que l'on attend d'elle
            */
          Case m_case;
          Etape * m_next;
    };
    Et, enfin, intéressons nous d'un peu plus près à la classe Chemin.

    Sa responsabilité est de maintenir un ensemble d'étapes par lesquelles il faut passer pour relier deux cases distinctes

    Les services que l'on attend d'elle sont donc que:
    1. On doit pouvoir la créer
    2. On doit pouvoir la détruire
    3. On peut vouloir être en mesure de la copier (*) (**)
    4. On peut vouloir être en mesure de l'assigner (*) (***)
    5. On veut pouvoir accéder à son premier élément
    6. On veut pouvoir accéder à... ce qui suit le dernier élément (****)
    7. On veut pouvoir ajouter un élément en fin de liste, si possible en temps constant
    8. ... il y a d'autres services dont on peut éventuellement avoir besoin (comme : permettre la recherche d'une étape passant par une case donnée, insérer une étape après une autre, vider la liste, ...)

    (*) ce n'est pas du tout obligatoire
    (**) si on le souhaite, il s'agira de faire en sorte que la copie et l'originale ne partagent pas les différents pointeurs (pour éviter les risques de fuite mémoire et de tentative de double libération de la mémoire)
    (***) si on le souhaite, il s'agira de faire en sorte que la mémoire qui était allouée aux éléments d'origine soit correctement libérée
    (****) le but est de permettre l'utilisation des boucle sous une forme dont l'utilisateur a l'habitude de les écrire

    Les détails d'implémentation qui nous permettront de rendre tous ces services seront:
    • Un pointeur sur... le premier élément de la liste
    • Un pointeur sur... le dernier élément de la liste (pour assurer l'ajout en temps constant)

    Au final, notre classe Chemin prendrait une forme (la précédente version était un peu incomplete ) proche de:
    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
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
     
    class Chemin
    {
        public:
            Chemin():m_first(NULL),m_last(NULL){}
            /* la copie doit se faire en profondeur, 
             * à moins qu'on ne l'interdise (1) 
             */
            Chemin(Chemin const & lhs)
            {
                // je profite des opérateur de Etape :D
                for(Etape* it=lhs.begin();it!=lhs.end();++it)
                    add(*it); 
     
            }
            /* l'assignation, à moins qu'on ne l'interdise (1)
             *
            Chemin & operator = (Chemin c) // copie inside
            {
                /* nécessite l'inclusion du fichier d'en-tête algorithm...
                 * l'idée est d'échanger les pointeurs m_first et m_last
                 * entre l'objet courent et la copie de l'objet à assigner
                 */
               std::swap(m_first,c.m_first);
               std::swap(m_last,c.m_last);
               /* c étant automatiquement détruit lorsque l'on passera
                * l'accolade fermantes, le destructeur de c sera appelé,
                * et la mémoire allouée aux différents éléments qui étaient
                * à l'origine dans l'objet courent sera correctement libérée
                */
            }
            /* le destructeur... doit veiller à correctement libérer la mémoire 
             * allouée aux différents éléments
             */
           ~Chemin()
           {
               while(m_first) //tant qu'il y a un premier élément 
               {
                   Etape * temp= m_first->m_next; // nous récupérons celui qui suit
                   delete m_first; // nous détruisons le premier élément
                   m_first=temp;  // et nous définissons celui qui le suivait comme
                                        // nouveau premier élément
               }
           } 
           /* pour ajouter un élément */
           void add(Case const & c)
           {
               /* nous créons dynamiquement une nouvelle étape  */
               Etape * temp=new Etape(c);
               /* C'est peut être le premier ajout que l'on fait...
                * dans ce cas, notre nouvelle étape devient... 
                * la première  de la liste
                */
               if(!m_first)
                   m_first = temp;
               /* si ce n'est pas le premier ajout, relions la dernière étape
                * à la nouvelle fraichement créée
                */
               if(m_last) 
                   m_last->m_next = temp;
               /* Quoi qu'il en soit, l'étape nouvellement créée devient...
                * la dernière du chemin
                */
               m_last = temp;
           }
           /* permet d'accéder à la première étape du chemin */
           Etape const * begin() const{return m_first;}
           /* permet d'accéder à ce qui suit la dernière étape */
           Etape const * end() const{return NULL;}
        private:
            /* les détails d'implémentation */
            Etape * m_first; // la première étape 
            Etape * m_last; // la dernière étape
    };
    (1) voir plus haut, ce qui est dit au sujet de la classe Etape

    La solution proposée ayant pour but de faire en sorte que chaque type d'objet n'aie jamais qu'une seule responsabilité et de "diriger" correctement l'utilisateur sur la manière d'utiliser notre chemin, en étant sur que tout ce qui doit être fait le soit au moment le plus opportun (et en gardant malgré tout une solution la plus simple possible).

    Ainsi, il pourra utiliser une boucle proche de
    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
    20
    21
    22
    23
    24
    25
    /* création d'un nouveau chemin */
    Chemin monChemin;
    /* plusieurs appels à add pour rajouter des "étapes" */
    for(Etape* it= monChemin.begin();it!=monChemin.end();
        ++it /* aurait pu être it=it->next() :D */ )
    {
        std::cout<<"ligne : "<<it->ligne() 
                 <<" colonne : " it->colonne()<<std::endl;
        /* pourrait prendre la forme de
       std::cout<<"ligne : "<<(*it).ligne() 
                 <<" colonne : " (*it).colonne()<<std::endl;
         
        std::cout<<"ligne : "<<it->data().ligne() 
                 <<" colonne : " it->data().colonne()<<std::endl;
        si l'opérateur -> et l'opérateur * ne sont pas définis
         */
     
    } 
    /* cela fonctionne aussi avec une boucle while */
    Etape * temp=chemin.begin();
    while(temp!=chemin.end())
    {
       /* utilisation de l'étape */
       ++temp;
    }
    En fait j'ai voulu afficher les éléments d'itinéraire ,comme suit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    void Itineraire::afficher(){
    	int v,d;
     
     
     
    	Chemin*	auxiliaire=depart;
    	cout<<"\n\n\n"<< auxiliaire->m_case.get_Ligne()<<"\n\n\n"<<endl;
    	 while(auxiliaire!=arrivee)
                {v=auxiliaire->get_case().get_Col();d=auxiliaire->get_case().get_Ligne();
    				cout<<("(")<<v<<(",")<<d<<("   ");
    				auxiliaire=auxiliaire->m_next;
    				}
    }
    C'est normal... Si j'en juge par ta structure chemin, cela ne devrait déjà pas compiler (elle ne dispose pas de la fonction membre get_case )
    or je me surpris que les valeurs des cases de départ changent dès la première utilisation de get_Ligne()!
    en notant bien que tu l'as utilisé dans ton code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      Chemin* temp=new Chemin(c);cout<<"\n\n\n"<<temp->m_case.get_Ligne()<<"\n\n\n"<<endl;
    et ça a bien marché.
    C'est aussi normal, new Chemin(c) appelle le constructeur de Chemin qui prend en paramètre... une référence constante sur un objet de type Case...

    C'est la mise en oeuvre d'un principe nommé RAII(Ressource Acquisition Is Initialisation) et dont le but est de faire en sorte que tout objet créé soit directement initialisé avec les valeurs utiles
    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

  7. #7
    Nouveau membre du Club
    Inscrit en
    Mai 2009
    Messages
    56
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 56
    Points : 29
    Points
    29
    Par défaut
    avant tout je veux dire quelque chose:
    pour moi (après avoir entendu tes conseils ):
    Etape est appelée chemin
    et Chemin est Itineraire!
    Donc Chemin aura le même code que tu as donné pour "Etape"

  8. #8
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 617
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 617
    Points : 30 636
    Points
    30 636
    Par défaut
    Citation Envoyé par dingua Voir le message
    avant tout je veux dire quelque chose:
    pour moi (après avoir entendu tes conseils ):
    Etape est appelée chemin
    et Chemin est Itineraire!
    Mais c'est, quelque part, incorrect du pur point de vue de la sémantique des termes...

    Chemin et itinéraire ont une sémantique similaire, ce qui fait que, en C++, nous pourrions envisager un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef Chemin Itineraire;
    alors que étape et chemin (ou itinéraire) représentent bel et bien deux choses clairement différentes

    Lorsqu'il s'agit de concevoir une application, il importe de faire AUSSI attention à ce genre de détails
    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

  9. #9
    Nouveau membre du Club
    Inscrit en
    Mai 2009
    Messages
    56
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 56
    Points : 29
    Points
    29
    Par défaut
    pour me comprendre voici les deux codes:
    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
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    #include "Case.h"
    class Chemin
    {
        /* cette amitié nous permet de ne pas exposer plus que nécessaire
         * l'existance du pointeur m_next
         */
        friend class Itineraire;
    	 private:
    		Case m_case;
            Chemin *m_next;
            /* les constructeurs et le destructeur sont déclarés private, de manière
             * à n'en autoriser l'accès qu'à la classe Itineraire
             */
    public:   
     
    	Case get_case()const ;
    	void set_next(Chemin* const );
    	Chemin();
    	~Chemin();
            /* le constructeur par copie et l'opérateur d'affectation sont déclarés
             * private et non définis (en attendant C++0x)
             */
            Chemin(Chemin const &);
          //  Chemin& operator=(Chemin const &);
            /* Ce qui nous permet de passer à l'itérateur suivant
             * (intervenant dans la sémantique de pointeur que l'on donne à notre
             * classe)
             */
        /* un constructeur appelant le constructeur de Case */
    		Chemin(int ligne, int colonne):m_case(ligne,colonne){};
            /* un autre se basant sur le constructeur par copie de Case */
            Chemin(Case const & c):m_case(c){};
            Chemin * operator++(){return m_next;};
            /* les opérateurs nous permettant de manipuler l'itérateur comme
             * s'il s'agissait d'un pointeur
             */
           //Case & operator *(){return m_case;};
           //Case const & operator *() const{return m_case;};
      //     Case * operator ->(){return & (operator *());};
        //   Case const * operator ->() const {return & (operator*());};
     
     
    };
    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
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    Itineraire::Itineraire():depart(NULL),arrivee(NULL){}
    Itineraire::Itineraire(Chemin* depart,Chemin* arrivee):depart(depart),arrivee(arrivee){
    	depart->set_next(arrivee);
    }
     
     
    Itineraire::~Itineraire(void)
    {
                while(depart)
                {
                    Chemin* temp=depart->m_next;
                    delete depart;
                    depart=temp;
                }
     
    }
    void Itineraire::add(Case const & c)
            {
     
                Chemin* temp=new Chemin(c);cout<<"\n\n\n"<<temp->m_case.get_Ligne()<<"\n\n\n"<<endl;
    			if((temp->m_case.get_Ligne()!=arrivee->m_case.get_Ligne())||(temp->m_case.get_Col()!=arrivee->m_case.get_Col()))
    			{
               Chemin* auxiliaire=depart;
    		   while(auxiliaire->m_next!=NULL)
    			   auxiliaire=auxiliaire->m_next;
    		   auxiliaire->m_next=temp;
     
    			}
            }
     
    void Itineraire::afficher(){
    	int v,d;
     
    /*	Chemin* auxiliaire=new Chemin;
     
    	auxiliaire->laCase=new Case(0,0);
    	auxiliaire->suivant=NULL;
     
    	auxiliaire=chemin;
     
     
    	for(auxiliaire=chemin; auxiliaire->suivant!=NULL; auxiliaire=auxiliaire->suivant){
     
    		cout<<("(")<<((auxiliaire->laCase)->get_Col())<<(",")<<((auxiliaire->laCase)->get_Ligne())<<("   ");
    	}*/
    	//Chemin* auxiliaire;//=new Chemin;
    	Chemin*	auxiliaire=depart;
    	cout<<"\n\n\n"<< auxiliaire->m_case.get_Ligne()<<"\n\n\n"<<endl;
    	 while(auxiliaire!=arrivee)
                {v=auxiliaire->get_case().get_Col();d=auxiliaire->get_case().get_Ligne();
    				cout<<("(")<<v<<(",")<<d<<("   ");
    				auxiliaire=auxiliaire->m_next;
    				}
    }

  10. #10
    Nouveau membre du Club
    Inscrit en
    Mai 2009
    Messages
    56
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 56
    Points : 29
    Points
    29
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Mais c'est, quelque part, incorrect du pur point de vue de la sémantique des termes...

    Chemin et itinéraire ont une sémantique similaire, ce qui fait que, en C++, nous pourrions envisager un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef Chemin Itineraire;
    alors que étape et chemin (ou itinéraire) représentent bel et bien deux choses clairement différentes

    Lorsqu'il s'agit de concevoir une application, il importe de faire AUSSI attention à ce genre de détails
    En fait ceci entre parmi les choses imposées!(vu que mon travail consiste à implémenter une conception déjà faite)

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 617
    Points : 30 636
    Points
    30 636
    Par défaut
    Citation Envoyé par dingua Voir le message
    En fait ceci entre parmi les choses imposées!(vu que mon travail consiste à implémenter une conception déjà faite)
    Hé bien, tu pourra "tirer les oreilles" de celui qui a fait la conception, car, si tu prend un dictionnaire, tu remarquera effectivement que ce qu'il essaye de faire passer pour un chemin est en réalité ce que j'ai appelé une étape
    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

  12. #12
    Nouveau membre du Club
    Inscrit en
    Mai 2009
    Messages
    56
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 56
    Points : 29
    Points
    29
    Par défaut
    Alors,la chose qui m'est apparue étonnante est que dans ce bout de code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    void Itineraire::afficher(){
    	int v,d;
    Chemin*	auxiliaire=depart;
    	cout<<"\n\n\n"<< auxiliaire->m_case.get_Ligne()<<"\n\n\n"<<endl;
    	 while(auxiliaire!=arrivee)
                {v=auxiliaire->get_case().get_Col();d=auxiliaire->get_case().get_Ligne();
    				cout<<("(")<<v<<(",")<<d<<("   ");
    				auxiliaire=auxiliaire->m_next;
    				}
    }
    Avant l'appel de la méthode "get_Ligne" la variable "depart" a la bonne valeur mais juste après(détecté par débogage)elle change en pointant nulle part!!

  13. #13
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 617
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 617
    Points : 30 636
    Points
    30 636
    Par défaut
    Citation Envoyé par dingua Voir le message
    pour me comprendre voici les deux codes:
    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
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    #include "Case.h"
    class Chemin
    {
        /* cette amitié nous permet de ne pas exposer plus que nécessaire
         * l'existance du pointeur m_next
         */
        friend class Itineraire;
    	 private:
    		Case m_case;
            Chemin *m_next;
            /* les constructeurs et le destructeur sont déclarés private, de manière
             * à n'en autoriser l'accès qu'à la classe Itineraire
             */
    public:   
     
    	Case get_case()const ;
    	void set_next(Chemin* const );
    	Chemin();
    	~Chemin();
            /* le constructeur par copie et l'opérateur d'affectation sont déclarés
             * private et non définis (en attendant C++0x)
             */
            Chemin(Chemin const &);
          //  Chemin& operator=(Chemin const &);
            /* Ce qui nous permet de passer à l'itérateur suivant
             * (intervenant dans la sémantique de pointeur que l'on donne à notre
             * classe)
             */
        /* un constructeur appelant le constructeur de Case */
    		Chemin(int ligne, int colonne):m_case(ligne,colonne){};
            /* un autre se basant sur le constructeur par copie de Case */
            Chemin(Case const & c):m_case(c){};
            Chemin * operator++(){return m_next;};
            /* les opérateurs nous permettant de manipuler l'itérateur comme
             * s'il s'agissait d'un pointeur
             */
           //Case & operator *(){return m_case;};
           //Case const & operator *() const{return m_case;};
      //     Case * operator ->(){return & (operator *());};
        //   Case const * operator ->() const {return & (operator*());};
     
     
    };
    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
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    Itineraire::Itineraire():depart(NULL),arrivee(NULL){}
    Itineraire::Itineraire(Chemin* depart,Chemin* arrivee):depart(depart),arrivee(arrivee){
    	depart->set_next(arrivee);
    }
     
     
    Itineraire::~Itineraire(void)
    {
                while(depart)
                {
                    Chemin* temp=depart->m_next;
                    delete depart;
                    depart=temp;
                }
     
    }
    void Itineraire::add(Case const & c)
            {
     
                Chemin* temp=new Chemin(c);cout<<"\n\n\n"<<temp->m_case.get_Ligne()<<"\n\n\n"<<endl;
    			if((temp->m_case.get_Ligne()!=arrivee->m_case.get_Ligne())||(temp->m_case.get_Col()!=arrivee->m_case.get_Col()))
    			{
               Chemin* auxiliaire=depart;
    		   while(auxiliaire->m_next!=NULL)
    			   auxiliaire=auxiliaire->m_next;
    		   auxiliaire->m_next=temp;
     
    			}
            }
     
    void Itineraire::afficher(){
    	int v,d;
     
    /*	Chemin* auxiliaire=new Chemin;
     
    	auxiliaire->laCase=new Case(0,0);
    	auxiliaire->suivant=NULL;
     
    	auxiliaire=chemin;
     
     
    	for(auxiliaire=chemin; auxiliaire->suivant!=NULL; auxiliaire=auxiliaire->suivant){
     
    		cout<<("(")<<((auxiliaire->laCase)->get_Col())<<(",")<<((auxiliaire->laCase)->get_Ligne())<<("   ");
    	}*/
    	//Chemin* auxiliaire;//=new Chemin;
    	Chemin*	auxiliaire=depart;
    	cout<<"\n\n\n"<< auxiliaire->m_case.get_Ligne()<<"\n\n\n"<<endl;
    	 while(auxiliaire!=arrivee)
                {v=auxiliaire->get_case().get_Col();d=auxiliaire->get_case().get_Ligne();
    				cout<<("(")<<v<<(",")<<d<<("   ");
    				auxiliaire=auxiliaire->m_next;
    				}
    }
    Il y a déjà certaines inconsistances entre les commentaires et la réalité du code...

    En effet, pour ce qui a trait à la classe Chemin, on lit le commentaire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    /* les constructeurs et le destructeur sont déclarés private, de manière
     * à n'en autoriser l'accès qu'à la classe Itineraire
     */
    alors que l'on remarque que les constructeurs et destructeurs sont déclarés publiques.

    De même, on constate un commentaire indiquant
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    /* le constructeur par copie et l'opérateur d'affectation sont déclarés
     * private et non définis (en attendant C++0x)
     */
    alors que, si le constructeur par copie n'est effectivement pas défini, il est déclaré en... public, et que la déclaration de l'opérateur d'affectation est... commentée (et donc considérée comme inexistante )

    Et, même si ce n'est qu'un détail, le commentaire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    /* Ce qui nous permet de passer à l'itérateur suivant
     * (intervenant dans la sémantique de pointeur que l'on donne à notre
     * classe)
     */
    s'applique à... l'opérateur ++... Ce serait pas mal de déplacer le commentaire pour le mettre juste avant l'opérateur ++

    Tout cela puor dire que, Généralement, lorsque je mets des commentaires dans un code (et j'en mets parfois beaucoup ), c'est pour attirer l'attention du lecteur de mon code sur certains points particuliers qui valent la peine d'être pris en considération, et non pour "faire joli" (et encore moins pour le plaisir de "perdre mon temps")

    Citation Envoyé par dingua Voir le message
    Alors,la chose qui m'est apparue étonnante est que dans ce bout de code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    void Itineraire::afficher(){
    	int v,d;
    Chemin*	auxiliaire=depart;
    	cout<<"\n\n\n"<< auxiliaire->m_case.get_Ligne()<<"\n\n\n"<<endl;
    	 while(auxiliaire!=arrivee)
                {v=auxiliaire->get_case().get_Col();d=auxiliaire->get_case().get_Ligne();
    				cout<<("(")<<v<<(",")<<d<<("   ");
    				auxiliaire=auxiliaire->m_next;
    				}
    }
    Un petit conseil d'ami...

    Essaye, autant que faire se peut, de prendre l'habitude de ne mettre qu'une seule instruction par ligne, et de respecter une politique stricte d'indentation.

    Et, tant qu'à faire, prend aussi l'habitude de supprimer le code "périmé" plutôt que de le commenter, de manière à ne laisser que les commentaires utiles dans ton code.

    Cela te facilitera énormément la vie lorsqu'il s'agira de "revenir" sur un code que tu as laissé sur le coté un certain temps.

    Et, en attendant, cela facilitera la vie des gens qui essaye de t'aider sur le forum

    Ainsi, ton code serait surement plus lisible sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    void Itineraire::afficher(){
        int v;
        int d;
        Chemin* auxiliaire=depart;
        cout<<"\n\n\n"<< auxiliaire->m_case.get_Ligne()
              <<"\n\n\n"<<endl;
        while(auxiliaire!=arrivee)
        {
            v=auxiliaire->get_case().get_Col();
            d=auxiliaire->get_case().get_Ligne();
            cout<<("(")<<v<<(",")<<d<<("   ");
            auxiliaire=auxiliaire->m_next;
        }
    }
    (avoue que ca devient tout de suite plus lisible, non )

    Cela met en avant quelques problèmes et inconsistances:

    Par exemple, on est en droit de se poser la question de savoir si l'acces à la case doit se faire sous la forme de
    , comme tu le fais juste après avoir déclaré et défini auxiliaire, ou s'il doit se faire sous la forme de
    comme tu le fais dans la boucle...
    Enfin, il y a
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    cout<<("(")<<v<<(",")<<d<<("   ");
    Heuuu... Ca compile, ca
    Avant l'appel de la méthode "get_Ligne" la variable "depart" a la bonne valeur mais juste après(détecté par débogage)elle change en pointant nulle part!!
    Peut-être faudrait il déjà résoudre les différents problèmes que je viens d'exposer avant de vouloir aller plus loin
    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

  14. #14
    Nouveau membre du Club
    Inscrit en
    Mai 2009
    Messages
    56
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 56
    Points : 29
    Points
    29
    Par défaut
    Oui je l'avoue j'ai mal modifié votre code Monsieur..

  15. #15
    Nouveau membre du Club
    Inscrit en
    Mai 2009
    Messages
    56
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 56
    Points : 29
    Points
    29
    Par défaut
    Maintenant ,j'ai fait les modifications nécessaires!
    d'où on peut passer à notre cas "étonnant",d'où une ligne de code suffira:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Chemin* auxiliaire=new Chemin(depart->m_case);
    et après construction depart change de valeur .


    NB:j'ai modifié le constucteur Chemin(Case const&)par:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Chemin(Case const & c):m_case(c){}
    *
    en éliminant l'itialisation de m_next à NULL car il y m'affiche une erreur(variable non déclarée).

  16. #16
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 617
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 617
    Points : 30 636
    Points
    30 636
    Par défaut
    Citation Envoyé par dingua Voir le message
    NB:j'ai modifié le constucteur Chemin(Case const&)par:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Chemin(Case const & c):m_case(c){}
    *
    en éliminant l'itialisation de m_next à NULL car il y m'affiche une erreur(varaible non déclarée).
    Ca, c'est déjà surprenant...

    Car, si m_next n'est pas déclaré, comment cela se fait-il que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    auxiliaire=auxiliaire->m_next;
    compile

    Il me semblerait donc judicieux, pour que l'on soit tous certains de partir sur les mêmes bases, que tu "nettoie" ton code de manière à ne garder que ce qui est d'actualité, que tu que tu organise "correctement" les différents commentaires pour qu'il précèdent tout juste ce à quoi ils ont trait, et que tu nous redonne l'ensemble complet (définition des classes et implémentation des fonctions membres)

    Le tout en n'oubliant pas d'indenter correctement et "de manière cohérente" le code

    De cette manière, nous éviterons de jouer aux devinettes pendant trop longtemps
    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

  17. #17
    Nouveau membre du Club
    Inscrit en
    Mai 2009
    Messages
    56
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 56
    Points : 29
    Points
    29
    Par défaut
    oki ,alors :
    Chemin: ( Etape pour vous)
    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
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    #include "Case.h"
    class Chemin
    {
        /* cette amitié nous permet de ne pas exposer plus que nécessaire
         * l'existance du pointeur m_next
         */
        friend class Itineraire;
    	friend class Robot;
        public:
            Chemin * operator++(){return m_next;};
            /* les opérateurs nous permettant de manipuler l'itérateur comme
             * s'il s'agissait d'un pointeur
             */
           Case & operator *(){return m_case;};
           Case const & operator *() const{return m_case;};
           Case * operator ->(){return & (operator *());};
           Case const * operator ->() const {return & (operator*());};
        private:
            /* les constructeurs et le destructeur sont déclarés private, de manière
             * à n'en autoriser l'accès qu'à la classe Itineraire
             */
            /* un constructeur appelant le constructeur de Case */
            Chemin(int ligne, int colonne):m_case(ligne,colonne),m{}
            /* un autre se basant sur le constructeur par copie de Case */
            Chemin(Case const & c):m_case(c){}
            /* le constructeur par copie et l'opérateur d'affectation sont déclarés
             * private et non définis (en attendant C++0x)
             */
            Chemin(Chemin const &);
            Chemin& operator=(Chemin const &);
            /* Ce qui nous permet de passer à l'itérateur suivant
             * (intervenant dans la sémantique de pointeur que l'on donne à notre
             * classe)
             */
            Case m_case;
            Chemin* m_next;
    };
    et
    Itineraire : ( Chemin pour vous)
    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
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    Itineraire::Itineraire():depart(NULL),arrivee(NULL){}
    Itineraire::Itineraire(Chemin* depart,Chemin* arrivee):depart(depart),arrivee(arrivee){
    	depart->m_next=arrivee;
    }
     
     
    Itineraire::~Itineraire(void)
    {
                while(depart)
                {
                    Chemin* temp=depart->m_next;
                    delete depart;
                    depart=temp;
                }
     
    }
    void Itineraire::add(Case const & c)
            {
     
                Chemin* temp=new Chemin(c);
    			if((temp->m_case.get_Ligne()!=arrivee->m_case.get_Ligne())||(temp->m_case.get_Col()!=arrivee->m_case.get_Col()))
    			{
               Chemin* auxiliaire=depart;
    		   while(auxiliaire->m_next!=NULL)
    			   auxiliaire=auxiliaire->m_next;
    		   auxiliaire->m_next=temp;
     
    			}
            }
     
    void Itineraire::afficher(){
     
     
    /*	Chemin* auxiliaire=new Chemin;
     
    	auxiliaire->laCase=new Case(0,0);
    	auxiliaire->suivant=NULL;
     
    	auxiliaire=chemin;
     
     
    	for(auxiliaire=chemin; auxiliaire->suivant!=NULL; auxiliaire=auxiliaire->suivant){
     
    		cout<<("(")<<((auxiliaire->laCase)->get_Col())<<(",")<<((auxiliaire->laCase)->get_Ligne())<<("   ");
    	}*/
    	//Chemin* auxiliaire;//=new Chemin;
    	Chemin* auxiliaire=new Chemin(depart->m_case);
     
     
     
     
    	 while(auxiliaire!=arrivee)
                {
    				cout<<("(")<<auxiliaire->m_case.get_Col()<<(",")<<auxiliaire->m_case.get_Ligne()<<(")")<<endl;
    				auxiliaire=auxiliaire->m_next;
    				}
    }

  18. #18
    Nouveau membre du Club
    Inscrit en
    Mai 2009
    Messages
    56
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 56
    Points : 29
    Points
    29
    Par défaut
    Voyez vous le problème?

  19. #19
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 617
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 617
    Points : 30 636
    Points
    30 636
    Par défaut
    Bon, il y a déjà un problème dans le constructeur de Chemin:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Chemin(int ligne, int colonne):m_case(ligne,colonne),m{}
    ce devrait être
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Chemin(int ligne, int colonne):m_case(ligne,colonne),m_next(NULL){}
    car il n'existe aucun membre m dans la classe Chemin

    De même, il faudrait veiller à initialiser m_next à NULL dans le constructeur
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Chemin(Case const & c):m_case(c){}
    sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Chemin(Case const & c):m_case(c),m_next(NULL){}
    de manière à être sur que le dernier élément de la liste soit relié à une adresse connue pour son invalidité, au lieu de laisser croire qu'il peut être relié à un élément inexistant

    (je soupçonne fortement que ce que le message qui te disait que m n'existe pas portait en réalité sur le premier constructeur et non sur le second )

    J'aurais aussi voulu avoir la définition de la classe Itineraire, car, c'est elle qui semble te poser le plus de problèmes.

    Mais, l'un dans l'autre, voici ce que je peux déjà dire:

    Le constructeur
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Itineraire::Itineraire(Chemin* depart,Chemin* arrivee):depart(depart),arrivee(arrivee){
    	depart->m_next=arrivee;
    }
    est, pour le moins, surprenant et n'a (déjà) pas lieu d'être: lorsque tu crée une itinéraire, elle est, par défaut, vide.

    Il n'est pas impossible que tu veuille créer un itinéraire en lui passant directement un pointeur sur un chemin d'origine et un autre sur un chemin de destination (respectivement le point de départ et le point d'arrivée), mais, comme la construction d'un objet de type Chemin est prise en charge par l'objet qui sera chargé de sa destruction (autrement dit par un objet de type Itineraire ou de type Robot, uniquement), tu ne peux pas te permettre de te contenter d'assigner l'adresse contenue par les deux pointeurs aux membres respectifs.

    En effet, si tu travaille ainsi, tu va te trouver face à une situation problématique dans laquelle deux instances de type Itineraire distinctes partagent leurs pointeurs de type Chemin.

    De ce fait, lors de la destruction de la première instance de type Itineraire, la mémoire allouée aux objet de type Chemin sera correctement libérée, mais...

    La deuxième instance utilisera toujours les mêmes adresses mémoires, qui ... deviendront invalides.

    Dés lors, toute tentative d'accès à ces pointeurs depuis l'instance "survivante" t'enverra "cueillir des pâquerettes", et, lorsque viendra le moment de détruire cette deuxième instance, tu te trouvera face à une tentative de double libération de la mémoire.

    En outre, il n'y a strictement rien qui puisse t'inciter à croire qu'il n'y aura jamais que deux éléments à rajouter par cette méthode, et le fait de faire pointer depart->m_next vers arrive occasionnera une belle fuite mémoire et la perte irrémédiable (pour les deux instances) de l'ensemble des éléments se trouvant entre le premier et le dernier.

    Tout cela pour dire que, si tu veux donner cette possibilité, tu dois assurer une copie de l'ensemble des éléments se trouvant entre les deux pointeurs transmis en paramètres (ces deux pointeurs étant inclus), un peu à la manière de ce que j'ai fait pour le constructeur par copie de mon intervention précédente

    Cela donnerait donc un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    Itineraire::Itineraire(Chemin * d, Chemin * a):depart(NULL),arrivee(NULL)
    {
        Chemin * temp = d;
        while(d)
        {
            add(*temp);
            ++temp;
        }
    }
    Enfin, pour ce que j'en lis, tu ne profite absolument pas du fait que tu dispose d'un pointeur sur... le dernier élément de la liste lors de l'ajout d'un élément, ce qui est dommage. (cf ma toute première intervention) et tu va, réellement, chercher de la difficulté là où il ne devrait pas y en avoir.

    En effet, les lignes de code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    			if((temp->m_case.get_Ligne()!=arrivee->m_case.get_Ligne())||(temp->m_case.get_Col()!=arrivee->m_case.get_Col()))
    			{
               Chemin* auxiliaire=depart;
    		   while(auxiliaire->m_next!=NULL)
    			   auxiliaire=auxiliaire->m_next;
    (qui ne sont décidément pas indentées de manière cohérente) ne servent décidément à rien, vu que tu a la certitude que:
    1. le premier élément est représenté par depart
    2. le dernier élément est représenté par arrivee
    et cela, même si depart et arrivee pointent en réalité vers... le même objet (ce qui arrive lorsque tu ajoute... le premier élément de la liste)

    La fonction add devrait donc prendre la forme de (je ne la réécrit que pour la troisième fois, allez, avec les commentaires pour te permettre de comprendre et en l'adaptant à tes classes existantes )
    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
    20
    void Itineraire::add(Case const & c)
    {
        /* nous créons dynamiquement un nouveau chemin  */
        Chemin * temp=new Chemin(c);
        /* C'est peut être le premier ajout que l'on fait...
         * dans ce cas, notre nouveau chemin devient... 
         * le premièr de la liste
         */
        if(!m_first) // équivalent à if (depart == NULL)
            m_first = temp;
        /* Si arrivee existe (ce qui revient à dire que ce n'est pas le
         * premier ajout) nous relions le dernier chemin à celui nouvellement créé
         */
        if(arrivee) // équivalent à if (arrivee != NULL)
            arrivee->m_next = temp;
        /* En fin de compte, et quoi qu'il advienne, le chemin nouvellement
         * créé devient... le dernier de l'itinéraire
         */
        arrivee = temp;
    }
    (au passage, tu remarquera que je respecte des règles d'indentation strictes: tout ce qui se trouve à un même niveau d'imbriquation commence à la même distance du bord gauche du code )

    Et, pour finir, la fonction afficher...

    D'abord, je voudrais dire que je ne suis pas vraiment favorable au fait de décider "unilatéralement" que l'affichage se fera d'office sur... la sortie standard.

    En effet, tu peux très bien vouloir provoquer l'affichage sur une autre sortie (un fichier texte, par exemple )

    Or si tu choisi de prendre cette décision unilatérale, lorsque la nécessité de provoquer une sortie vers un fichier texte (pour continuer avec mon exemple), tu devra rajouter une fonction travaillant explicitement sur un fichier mais... ayant exactement le même comportement que ta fonction afficher (hormis le fait qu'elle utilisera un autre flux de sortie).

    Dés lors, pourquoi ne pas écrire directement une fonction qui pourra s'adapter à n'importe quel flux de sortie

    Cela permettrait même le cas échant, si un jour tu dois passer par un "flux réseau", de garder la même fonction...

    Avoue que c'est vraiment de nature à nous simplifier la vie, non

    Et ce qui est chouette, c'est que tous les flux de sortie ont une origine commune: la classe ostream.

    Ensuite, je voudrais attirer ton attention sur le fait que, idéalement, une fonction d'affichage devrait s'engager... à ne pas modifier l'objet en cours.

    Cela rentre dans tout un concept que les anglo-saxons appellent le "const-correctness" (aucune traduction française ne me plait outre mesure) et qui consiste à déclarer constant tout ce qui n'a pas vocation à être modifié.

    De cette manière, le compilateur, qui sera toujours plus bûté que toi, refusera toute tentative de modification qui pourrait survenir par erreur alors que tu t'es engagé à ne justement pas apporter de modification.

    Pour indiquer au compilateur qu'une fonction membre d'une classe s'engage à ne pas modifier l'objet en cours, il "suffit" de faire suivre la parenthèse fermante par le mot clé const, ce qui nous donnerait, pour la fonction afficher, quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class Itineraire
    {
        public:
            void afficher() const;
        /* tout le reste */
    };
    et une implémentation proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void Itineraire::afficher() const
    {
        /*contenu de la fonction*/
    }
    La dernière remarque que je ferai au sujet de cette fonction, c'est que je constate que, là encore, tu n'a pas "purgé" ton code des parties qui n'ont plus lieu d'être...

    Je sais que j'ai l'air d'un "vieux con moralisateur", mais, s'il y a une chose dont je tiens à te persuader, c'est que la première qualité d'un code doit être, avant même de faire ce que l'on attend de lui, d'être facilement lisible par la personne qui l'a sous les yeux.

    En effet, ce n'est pas un programme qui va devoir apporter des modifications à un code donné (même si certains sont en mesure de le réindenter), mais... un être humain, avec toutes ses qualités et ses faiblesses.

    Et cet humain, ce peut être toi, dans trois mois ou un an... ou plus...

    Or, le code que tu écris aujourd'hui te semble "parfaitement clair" là, à l'instant, parce que tu l'a bien en tête, mais, dans trois mois, alors que tu auras écrit des centaines ou des milliers d'autres lignes de code (ou passé des heures à te casser la tête sur d'autres cours, ou ...), si ton code est truffé de partie "désactivées" (parce que mises en commentaire), s'il ne respecte pas une politique stricte de mise en forme, s'il ne contient pas des commentaires clairs sur ce que tu veux faire, si les noms de variables et de fonctions ne sont pas explicites, tu va perdre beaucoup plus de temps à essayer de "te rappeler" de ce que tu voulais faire que ce que tu *crois* gagner aujourd'hui comme temps à écrire "à la va vite".

    Et encore, je ne parle ici que d'un code que tu écris toi-même, mais, imagine si le code est écrit par quelqu'un d'autre...

    Enfin bref, cela n'apporte pas encore de solution à ton problème, mais je tenais malgré tout à attirer ton attention sur ce point

    La première chose qui me choque à la lecture du code de ta fonction afficher (ou plutôt la première grosse erreur, dirons nous ), c'est que... tu crées un nouvel objet de type Chemin, alors que tu n'en a absolument pas besoin.

    Et, comme tu te base sur ce nouvel objet pour toute la suite, fatalement, tout le reste de la logique qui suit devient tout à fait foireux. (sans compter le fait que ce nouvel objet n'est jamais détruit, et que l'on assiste donc à une fantastique fuite mémoire )

    En effet, la logique consiste, tout simplement, à
    1. prendre le premier élément de la liste,
    2. le faire afficher
    3. puis à passer au suivant,
    4. recommencer en (2) jusqu'à ce qu'il n'y ait plus d'élément à afficher

    En prenant toutes ces remarques en compte, la fonction affiche pourrait être définie comme suit:
    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
    20
    21
    22
    23
    void Itineraire::afficher(ostream &ofs) const
    {
        /* d'abord, nous déclarons un pointeur de travail que nous
         * initialisons avec l'adresse contenue par... le premier élément
         * de la liste
         */
        Chemin * travail = depart;
        /* tant qu'il y a un élément à afficher, nous entrons dans la boucle
         */
       while(travail) // équivalent à while (travail !=NULL)
       {
           /* nous envoyons dans le flux sortant les informations concernant
            * la case contenue par le chemin en cours
            */
           ofs<< "("<<travail->m_case.get_Col()
              <<","<<travail->m_case.get_Linge()
              <<")"<<std::endl;
           /* et nous passons à l'élément suivant */
           ++travail;
     
     
       }
    }
    Normalement, et pour autant que le code que tu nous a présenté soit effectivement le code sur lequel tu te base (n'oublie pas qu'il faut relancer la compilation chaque fois que tu apporte une modification au code ), si tu apporte les quelques modifications que je viens de te présenter, il n'y a aucune raison pour que ton code ne compile pas, ni pour qu'il ne réagisse pas exactement comme tu l'attend.
    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

  20. #20
    Nouveau membre du Club
    Inscrit en
    Mai 2009
    Messages
    56
    Détails du profil
    Informations forums :
    Inscription : Mai 2009
    Messages : 56
    Points : 29
    Points
    29
    Par défaut
    Je n'ai qu'à vous dire Monsieur Merci...et tous vos commentaires m'appellent vraiment à faire de recul pour être un vrai programmeur..
    encore Merci!

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Programme pour ajouter et supprimer élément à une liste
    Par basset1991 dans le forum Général Java
    Réponses: 3
    Dernier message: 15/12/2013, 23h12
  2. Ajouter un élément à une liste
    Par izulah dans le forum Prolog
    Réponses: 5
    Dernier message: 17/03/2009, 08h57
  3. Ajouter un élément à une liste
    Par circe dans le forum R
    Réponses: 2
    Dernier message: 24/10/2008, 18h00
  4. Ajout élément à une liste
    Par Parkman dans le forum InfoPath
    Réponses: 11
    Dernier message: 14/05/2008, 08h41
  5. Réponses: 22
    Dernier message: 24/11/2006, 00h36

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