Liste chainnée C++

Fermé
yalmazo1 Messages postés 1 Date d'inscription mardi 29 janvier 2013 Statut Membre Dernière intervention 29 janvier 2013 - Modifié par mamiemando le 1/02/2013 à 00:39
mamiemando Messages postés 33093 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 4 mai 2024 - 1 févr. 2013 à 01:03
Bonjour,

Je suis débutante en c++, mais j'ai des connaissance en java et en C , je doit modifier ce programme pr créer une liste circulaire .

template <class Elem>     
class Link {      
  public :      
    Elem element ;      
    Link * next ;      

    Link (     
      const Elem & elemval,      
      Link* nextval = NULL     
    ) {      
      element = elemval ;     
      next = nextval ;     
    }      

    Link(Link * nextval = NULL) {     
      next = nextval ;     
    }      
}; 


Le programme ci-dessus est fait pour créer une liste chaînée, et je sais qu'il faut ajouter un pointeur vers le premier élément.
Ma question c'est au niveau des lignes en gras j'ai pas très bien saisi la syntaxe.

Merci pour votre aide.

1 réponse

mamiemando Messages postés 33093 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 4 mai 2024 7 752
Modifié par mamiemando le 1/02/2013 à 01:04
Les lignes en gras correspondent à deux déclarations de constructeurs pour la classe Link.
- Le premier attend une const référence sur une instance de type Elem, et éventuellement un pointeur initialisé s'il est omis à NULL.
- Le second attend un pointeur optionnel qui est par défaut initialisé à NULL.

Vu que tu as fait du java tu sais ce qu'est une référence, puisqu'en java tous les passages de paramètres se font par référence. Le mot clé "const" signifie que la valeur référencée ne sera pas modifiée par la fonction.

En C++ tu as aussi les passages par recopie et les passages par pointeurs. En terme de syntaxe un passage par recopie et un passage par référence se comportent syntaxiquement de la même façon. Au niveau du comportement en lui même par contre, c'est plutôt le passage par référence et par pointeur qui se comportent de la même manière. La principale différence entre une référence et un pointeur, c'est qu'une référence doit toujours être initialisée et référencer une instance (tandis qu'un pointeur peut valoir NULL).

De manière générale écrire "toto *" signifie qu'on parle d'une adresse et qu'à cette adresse, on s'attend à trouver un objet de type toto. Quand on parle de "toto &", tout se passe comme si on manipulait une adresse et qu'on s'attendait à y trouver un objet de type toto. Enfin quand on parle de "toto", on recopie dans la pile un objet de taille toto, et donc la fonction travaille sur une recopie.

Ce petit exemple illustre la syntaxe et le comportement des trois types de passage de parametres

#include <iostream>  

// par recopie  
void incrementer1(int x) {  
  std::cout << "dans incrementer1:" << x << std::endl; // affiche 7  
  x++;  
  std::cout << "dans incrementer1:" <<  << x << std::endl; // affiche 8  
}  

// par référence  
void incrementer2(int & x) {  
  x++;  
}  

// par pointeur  
void incrementer(int *px) {  
  (*px)++;  
}  

int main() {  
  int x = 7;  
  incrementer1(x);  
  std::cout << x << std::endl; // affiche 7  
  incrementer2(x);  
  std::cout << x << std::endl; // affiche 8  
  incrementer3(&x);  
  std::cout << x << std::endl; // affiche 9  
  return 0;  
}


Lors de l'appel d'incrementer1, le x du main est recopié dans la pile. C'est cette recopie qui est altérée par incrementer1. Modifier x dans incrementer1 ne modifie donc pas la valeur de x dans le main, ce sont deux variables différentes.

Dans incrementer2, on fait un passage par référence. Donc on parle bien de la même variable, et modifier dans incrementer2 modifie la valeur de x dans main (puisque c'est une seule et même variable).

Dans incrementer3, on passe (par recopie) l'adresse de x (que j'ai notée px). Il s'agit de l'adresse mémoire de la variable x déclarée dans le main. Ainsi quand je regarde ce qui est à cette adresse *px, je manipule bien la variable x déclarée dans main.

Maintenant retournons sur ton code. Dans ton constructeur, tu peux initialiser directement ses membres en faisant appel au constructeur par copie (dans le cas général pour une classe "toto_t", le constructeur par copie est "toto(const toto_t & x)") qui est défini par défaut. Dans le code actuel tu fais appel à un opérateur "=" qui n'est pas forcément défini. De plus, vu que tout est public, autant utiliser directement une struct.

Ainsi le code ressemblerait plutôt à (en améliorant un peu les notations) :

template <typename T>       
struct link_t {  
    T element;
    link_t * next;

    link_t (
      const T & element0,
      link_t * next0 = NULL
    ):
      element(element0),
      next(next0),
    {}

    link_t(link_t * next0 = NULL):
      element(), // cette ligne peut être sous-entendue
      next(next0),
    {}
}; 


Note que dans ton code ou celui-ci, on présuppose que la classe Elem dispose d'un constructeur par défaut (par exemple "toto_t()").

En terme de code, tu peux effectivement définir une classe "Link" qui correspond à un maillon de la liste (peu importe qu'elle soit simplement chaînée ou circulaire) et ensuite définir une classe list, une classe liste_circulaire etc... qui s'appuie dessus.

En tout cas dans la vraie vie, plutôt que de coder une liste chaînée on utiliserait directement la classe fournie par la STL (std::list).
https://forums.commentcamarche.net/forum/affich-37604421-introduction-a-la-stl-en-c-standard-template-library

Bonne chance
0