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

Langage C++ Discussion :

Classe pile generique avec template


Sujet :

Langage C++

  1. #1
    Nouveau membre du Club
    Inscrit en
    Décembre 2008
    Messages
    35
    Détails du profil
    Informations forums :
    Inscription : Décembre 2008
    Messages : 35
    Points : 26
    Points
    26
    Par défaut Classe pile generique avec template
    Salut a tous,j'ai un probleme avec les template ,j'ai réaliser une petite classe pile
    qui marche très bien mais quand j'ai voulus la rendre generique je suis tembé dans de plusieurs erreurs.

    J'ai voulues envoyer tous le projet en pièce jointe mais ça n'a pas marché,bon je l'envoie avec la balise code.
    S'il vous plais compiler le et montrer moi mes erreurs.

    Merci d'avance.

    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
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
     
    //le fichier noeud.h
    #include "iostream.h"
    #include "stdlib.h"
    #include "time.h"
     
     
    template <class T> class noeud
    {
    public:
    	noeud(T ielt=0,noeud<T>* isvt=NULL);
     
    private:
    	friend class pile;
    	T elt;
    	noeud<T>* svt;
    };
    //le fichier pile.h 
    #include "noeud.h"
     
    template <class T> class pile
    {
    public:
    	pile();
    	~pile();
    	void empiler(T);
    	void depiler();
    	void afficher();
    private:
    	noeud<T>* tete;
    };
    //le fichier pile.cpp
    #include "pile.h"
     
     
    pile<T>::pile()
    {
    	tete=NULL;
    }
    template <class T> pile<T>::~pile()
    {
    	while(tete!=NULL)
    	{
    		depiler();
    	}
    	delete tete;
    }
     
    template <class T> void pile<T>::empiler(T val)
    {
    	noeud<T>* n=new noeud<T>(val);
     
    	n->elt=val;
    	n->svt=tete;
    	tete=n;
    }
     
    template <class T> void pile<T>::depiler()
    {
    	noeud<T>* p;
    	if(tete!=NULL)
    	{
    		p=tete;
    		tete=tete->svt;
    	}
    	delete p;
    }
     
    template <class T> void pile<T>::afficher()
    {
    	if(tete==NULL)
    		cout<<"la pile est vide";
    	if(tete!=NULL)
    	{
    		noeud<T>* n=tete;
    		while(n!=NULL)
    		{
    			cout<<"<"<<n->elt<<"> "<<endl;
    			n=n->svt;
    		}
    	}
    	cout<<endl;
    }
    //le fichier noeud.cpp
    #include "noeud.h"
     
    template <class T> noeud<T>::noeud(T ielt,noeud<T>* isvt)
    {
    	elt=ielt;
    	svt=isvt;
    }
    //le fichier main.cpp
    #include "pile.h"
     
    main()
    {
    	pile <int> p;
     
    	p.empiler(7);
    	p.empiler(8);
    	p.empiler(9);
    	p.empiler(10);
    	p.afficher();
    	p.depiler();
    	p.depiler();
    	p.depiler();
    	p.depiler();
    	p.afficher();
     
     
    }

  2. #2
    Nouveau Candidat au Club
    Inscrit en
    Avril 2009
    Messages
    1
    Détails du profil
    Informations forums :
    Inscription : Avril 2009
    Messages : 1
    Points : 1
    Points
    1
    Par défaut
    Changè le code suivant

    friend class pile;

    pour

    template<typename U> friend class pile;

    C'est qui a provoqué erreurs dans mon vs2008.
    J'espère que aide vous.

  3. #3
    Nouveau membre du Club
    Inscrit en
    Décembre 2008
    Messages
    35
    Détails du profil
    Informations forums :
    Inscription : Décembre 2008
    Messages : 35
    Points : 26
    Points
    26
    Par défaut
    Merci pour la reponse, de 22 erreurs il en reste que 6,dont plusieurs erreurs
    <end parse> que je ne comprend pas.

  4. #4
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    Peux-tu poster l'intégralité du message d'erreur ?

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

    Informations professionnelles :
    Activité : aucun

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

    Je voudrais déjà attirer ton attention sur quelques points assez particuliers:

    Le premier est que l'implémentation des fonctions template doit être placée dans le fichier d'en-tête ou, de manière indirecte, dans un fichier (dont l'extension ne sera pas cpp), qui sera inclu dans le fichier d'en-tête.

    La raison en est qu'il est impossible pour le compilateur de créer le code binaire d'une fonction template (ou d'une fonction membre d'une classe template) tant qu'il ne sait pas quel type réellement utiliser.

    Il doit donc disposer du code des fonctions pour chaque implémentation particulière qu'il trouvera dans le projet.

    Pour y arriver, il faut, bah, que le code des fonctions soit accessible depuis le fichier d'en-tête

    Le deuxième point est un détail, mais, typiquement, la classe Noeud devrait être imbriqué dans la classe Pile.

    En effet, le noeud ne doit être manipulé que dans un contexte de pile, et il n'y a donc pas de raison particulière de laisser l'utilisateur de ta classe s'en servir autrement

    Cela se traduira par un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template <typename T>
    class Pile
    {
        public:
            template<typename T>
            Noeud
            {
                /* contenu du noeud */
            };
            /* contenu de Pile */
    };
    Le point suivant sur lequel je souhaiterais attirer ton attention est le constructeur de ton noeud:

    Non seulement, il est inutile de donner la valeur par défaut 0 à la valeur de l'élément (ielt), mais c'est également dangereux:

    C'est inutile parce que, il n'y a rien à faire, il faut disposer d'un élément valide lors de la construction du noeud (même si plusieurs points de vue devraient être envisagés): Si tu n'en dispose pas, tu ne peux pas décemment envisager d'initialiser ton objet de type Noeud : un noeud ne peut pas être "défaut constructible"

    C'est dangereux parce que tu semble considérer qu'une valeur nulle sera d'office cohérente avec une valeur par défaut, mais ce ne sera pas forcément le cas...

    Et nous pouvons presque considérer que cette valeur par défaut sera plus souvent incorrecte que cohérente dans le sens où elle ne sera cohérente que si l'élément est un type primitif ou un pointeur (et encore)

    De plus, il est inutile de passer un pointeur vers l'élément suivant (précédent, en fait, dans une pile) au constructeur, et partant de lui donner une valeur par défaut:

    Lorsque tu construit ton noeud, le pointeur vers le noeud suivant doit être d'office initialisé à NULL et seule la pile doit non seulement être capable d'y accéder, mais aussi savoir que ce pointeur existe

    Ensuite, il ne faut pas oublier que le noeud doit être copiable et assignable, mais que le comportement par défaut du compilateur n'est pas adapté à la copie, du fait de la présence du pointeur vers l'élément précédent dans la pile (il en est d'ailleurs de même pour la pile ):

    Le comportement implémenté par défaut par le compilateur est en effet la copie membre à membre, ce qui se traduit par le fait que, en cas de copie d'un noeud, tu te retrouvera avec plusieurs noeuds (l'original et la copie) pointants vers un seul et même noeud précédent.

    Cet état de fait a de fortes chances de mener très rapidement à des doubles libérations de la mémoire (le fait d'essayer d'invoquer plus d'une fois delete sur un élément alloué dynamiquement).

    Or une double libération de la mémoire mène systématiquement à des catastrophes, d'ampleur inconnue: cela peut, dans le meilleur des cas, mener au plantage de ton application, dans le pire, provoquer le lancement une ogive nucléaire sur Paris... ou pire encore

    Un dernier point à prendre en compte (enfin, pour l'instant, histoire de ne pas partir sur l'écriture d'un roman et te laisser l'occasion d'assimiler à ton aise les points évoqués), c'est qu'il est très bien de disposer d'un noeud, mais qu'il faut, aussi, s'assurer de pouvoir accéder à la valeur du noeud...

    Tu devra donc envisager de définir les opérateurs * et -> pour donner l'accès à cette valeur

    Malgré les apparences, je t'ai donné ici la version "courte" des explications.

    Je vais te laisser cogiter un peu dessus, histoire de te donner une chance de trouver la solution par toi-même.

    Mais n'hésite pas à poser toutes les questions que tu veux si un des points évoqué reste ténébreux pour toi

  6. #6
    Nouveau membre du Club
    Inscrit en
    Décembre 2008
    Messages
    35
    Détails du profil
    Informations forums :
    Inscription : Décembre 2008
    Messages : 35
    Points : 26
    Points
    26
    Par défaut
    Salut a tous ,j'ai grouper tout dans un header et il me reste 7 erreur que je n'ai pas compris.
    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
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    #include <iostream.h>
    #include <stdlib.h>
    #include <time.h>
     
     
    template <class T> class pile
    {
    public:
    	template <class T> class noeud
    	{
    	public:
            template <class T> noeud(T ielt,noeud<T>* isvt)
    		{
    			elt=ielt;
    			svt=isvt;
    		}
    	private:
    		T elt;
    		noeud<T>* svt;
    	};
        template <class T> pile()
    	{
    		tete=NULL;
    	}
    	~pile()
    	{
    		while(tete!=NULL)
    		{
    			depiler();
    		}
    		delete tete;
    	}
     
    	template <class T> void empiler(T val)
    	{
    		noeud<T>* n=new noeud<T>(val);
     
    		n->elt=val;
    		n->svt=tete;
    		tete=n;
    	}
     
    	template <class T> void depiler()
    	{
    		noeud<T>* p;
    		if(tete!=NULL)
    		{
    			p=tete;
    			tete=tete->svt;
    		}
    		delete p;
    	}
    	/*
    	ostream& operator<<(ostream out,pile& p)
    	{
    		if(p.tete!=NULL)
    		{
    			noeud* n=p.tete;
    			while(n!=NULL)
    			{
    				out<<"<"<<n->elt<<"> ";
    				n=n->svt;
    			}
    		}
    		return out;
    	}
    	*/
    	template <class T> void afficher()
    	{
    		if(tete==NULL)
    			cout<<"la pile est vide";
    		if(tete!=NULL)
    		{
    			noeud<T>* n=tete;
    			while(n!=NULL)
    			{
    				cout<<"<"<<n->elt<<"> "<<endl;
    				n=n->svt;
    			}
    		}
    		cout<<endl;
    	}
    	private:
    		noeud<T>* tete;
    };
    Le main()
    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
    #include "pile.h"
     
     
    main()
    {
    	pile <int> p;
     
    	p.empiler(7);
    	p.empiler(8);
    	p.empiler(9);
    	p.empiler(10);
    	p.afficher();
    	p.depiler();
    	p.depiler();
    	p.depiler();
    	p.depiler();
    	p.afficher();
     
     
    }
    les erreurs.
    :\Program Files\Microsoft Visual Studio\MyProjects\Pile T\main.cpp(6) : error C2512: 'pile<int>::pile<int>' : no appropriate default constructor available
    C:\Program Files\Microsoft Visual Studio\MyProjects\Pile T\main.cpp(12) : error C2783: 'void __thiscall pile<int>::afficher(void)' : could not deduce template argument for 'T'
    C:\Program Files\Microsoft Visual Studio\MyProjects\Pile T\main.cpp(13) : error C2783: 'void __thiscall pile<int>::depiler(void)' : could not deduce template argument for 'T'
    C:\Program Files\Microsoft Visual Studio\MyProjects\Pile T\main.cpp(14) : error C2783: 'void __thiscall pile<int>::depiler(void)' : could not deduce template argument for 'T'
    C:\Program Files\Microsoft Visual Studio\MyProjects\Pile T\main.cpp(15) : error C2783: 'void __thiscall pile<int>::depiler(void)' : could not deduce template argument for 'T'
    C:\Program Files\Microsoft Visual Studio\MyProjects\Pile T\main.cpp(16) : error C2783: 'void __thiscall pile<int>::depiler(void)' : could not deduce template argument for 'T'
    C:\Program Files\Microsoft Visual Studio\MyProjects\Pile T\main.cpp(17) : error C2783: 'void __thiscall pile<int>::afficher(void)' : could not deduce template argument for 'T'
    C:\Program Files\Microsoft Visual Studio\MyProjects\Pile T\main.cpp(18) : warning C4508: 'main' : function should return a value; 'void' return type assumed
    Error executing cl.exe.

    Pile T.exe - 7 error(s), 1 warning(s)

  7. #7
    Membre actif
    Étudiant
    Inscrit en
    Octobre 2007
    Messages
    189
    Détails du profil
    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2007
    Messages : 189
    Points : 213
    Points
    213
    Par défaut
    Bonsoir,

    tu devrais utiliser des typedef dans la classe pour éviter de devoir préciser T à chaque fois que le compilo ne peut pas le définir tout seul :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    p.afficher<int>();
    p.depiler<int>();
    D'où les erreurs 2 à 7.

    Le warning t'indique que ta fonction main n'a pas la bonne syntaxe et que ton compilo la transforme pour toi ( en qqch de pas très très propre ) .
    Tu devrais écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int main(int, char**) {
      // ...
      return 0;
    }

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 626
    Points : 30 684
    Points
    30 684
    Par défaut
    De plus, tu n'a absolument pas pris mes remarques en compte...

    Allez, bon prince, je vais te donner un coup de main pour une pile qui utilise un type nommé Type...

    Tu n'aura "plus qu'à" transformer cela en template (je ne vais quand meme pas tout faire pour toi non plus )
    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
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    class Pile
    {
        public:
            class Element
            {
                /* la pile doit pouvoir accéder à la référence vers l'élément
                 * précédent
                 */
                friend class Pile;
                /* mais rien d'autre ne peut y accéder */
                Element* previous;
                /* et il faut, bien sur, l'objet à empiler */
                Type value;
                public:
                    /* il faut pouvoir construire, copier et assigner
                     * un élément de la pile... la présence d'un pointeur sur
                     * l'élément suivant nous oblige à redéfinir nos propres
                     * constructeurs par copie et opérateur d'affectation
                     * le tout, sans oublier le constructeur "normal".
                     */
                    /* lorsque l'on crée l'élément de la pile, la référence vers
                     * l'élément précédentdoit etre initialisée à 0, pour
                     *  nous permettre de repérer le dernier élément de la pile
                     *  (qui est le premier a avoir été empilé)
                     */
                     Element(Type const& value):previous(0),value(value){}
                     Element(Element const & el):previous(0),value(el.value){}
                     Element& operator=(Element rhs)
                     {
     
                         value=rhs.value;
                         previous = 0;
                         return *this;
                     }
                     /* Il ne faut pas pouvoir accéder à l'élément précédent, mais
                      * il faut pouvoir accéder à l'objet empilé
                      */
                      Type & operator *(){return (this->value);}
                      Type const& operator*(Element* el) const{return this->value;}
                     /* un élément a sémantique de pointeur, la fleche est
                      * intéressante dans ce cadre
                      */
                     Type & operator ->(){return value;}
                     Type const & operator->() const{return value;}
            };
            Pile():phead(0){}
            /* le destructeur n'est inline que par flegme de ma part */
            ~Pile()
            {
                /* tant qu'il y a un élément à la tete, on le détruit */
                while(phead)
                {
                    Element* temp=phead->previous;
                    delete phead;
                    phead=temp;
                }
            }
            /* la fonction d'empilement aussi d'ailleurs */
            void empile(Type const& value)
            {
                Element * temp=new Element(value);
                temp->previous=phead;
                phead= temp;
            }
            bool empty() const{return phead==0;}
            size_t size() const
            {
                if(!phead)
                    return 0;
                size_t ret = 0;
                Element* temp=phead;
                while(temp)
                {
                    ++ret;
                    temp=temp->previous;
                }
                return ret;
            }
            void depile()
            {
                if(phead)
                {
                    Element* temp=phead->previous;
                    delete phead;
                    phead=temp;
                }
            }
            Element & head(){return *phead;}
            Element const & head() const{return *phead;}
        private:
            Element* phead;
    };
    (code écrit "from scratch, et non testé)

    N'hésite pas à poser des questions si besoin est

  9. #9
    Membre actif
    Étudiant
    Inscrit en
    Octobre 2007
    Messages
    189
    Détails du profil
    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2007
    Messages : 189
    Points : 213
    Points
    213
    Par défaut
    Petit détail pour pinailler : size_t et empty n'ont pas le bon type de retour. Tu as aussi inversé les noms next et previous.
    C'est moi ou operator= est dangereux ?

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 626
    Points : 30 684
    Points
    30 684
    Par défaut
    Citation Envoyé par hiura Voir le message
    Petit détail pour pinailler : size_t et empty n'ont pas le bon type de retour.
    Effectivement, le type de retour de empty est bool et le type de... size est... size_t...

    J'ai modifié le code enconséquence
    Tu as aussi inversé les noms [FONT=monospace][/FONT]next et previous.
    Effectivement, j'ai, dans un premier temps pensé à utiliser next, puis je me suis rendu compte que, la pile incarnant le système LIFO (Last In First Out), il était plus logique de considérer la référence vers l'élément adjacent sous le nom de previous, dans le sens où c'est l'élément rajouté... avant l'élément accessible

    Et j'ai oublié de le modifier à l'un ou l'autre endroit

    J'ai, là aussi, modifié le code en conséquence
    C'est moi ou operator= est dangereux ?
    Pas dans ce contexte, car il utilise une "copie" qui n'est pas complète...

    L'opérateur = est généralement dangereux lorsqu'une structure manipule des pointeurs car tu cours le risque que l'adresse pointée soit dupliquée, et donc de te mettre dans une situation dans laquelle tu effectue une double libération de la mémoire dont l'adresse est pointée.

    J'ai d'ailleurs modifié le code de manière à implémenter quelque chose de fort proche de l'idiome copy and swap, afin d'éviter tout problème

    Ici, la copie de Element ne fait que copier l'objet maintenu, en prenant soin d'initialiser le pointeur previous à nul.

    De plus, rien dans l'interface publique ne permet d'accéder d'une quelconque manière au pointeur previous.

    Le code verrouille donc toute possibilité d'en arriver à une double libération de la mémoire, pour autant que tu ne fais pas de bêtise au sein de pile, qui est la seule classe a réellement utiliser le pointeur previous

    La seule chose, c'est que l'on peut se poser la question de l'opérateur d'affectation, étant donné que, en l'état, nous travaillons principalement avec des pointeurs

    [EDIT]Suite à ton intervention, j'ai relu un peu plus consciencieusement mon code, et j'ai corrigé quelques erreurs d'attention supplémentaires

  11. #11
    Membre actif
    Étudiant
    Inscrit en
    Octobre 2007
    Messages
    189
    Détails du profil
    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2007
    Messages : 189
    Points : 213
    Points
    213
    Par défaut
    ça me rassure que tu aies modifié l'op=. [ A 1 heure j'étais pas trop sûr de dire que Element& operator=(Element rhs) { Element(rhs); } soit correcte, d'où mon message peut précis. ]

Discussions similaires

  1. Réponses: 10
    Dernier message: 11/04/2013, 16h22
  2. Réponses: 2
    Dernier message: 30/12/2009, 20h44
  3. Classe Liste générique avec template
    Par TNT89 dans le forum Langage
    Réponses: 9
    Dernier message: 09/05/2009, 15h14
  4. Difficultés avec template de classe
    Par Rniamo dans le forum Langage
    Réponses: 2
    Dernier message: 06/11/2008, 09h28
  5. Réponses: 9
    Dernier message: 19/05/2007, 15h28

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