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 :

Vos conseils et critiques


Sujet :

C++

  1. #1
    Membre éclairé
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    577
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 577
    Par défaut Vos conseils et critiques
    Bonjour,

    voilà comme je connaissais pas trop les template, j'ai décidé de m'y mettre un peu. Donc j'ai réalisé une classe pour la gestion des listes chaînées, ce qui peut être interessant pour stocker divers types d'infos.

    Pourriez-vous me donner vos conseils et critiques ?
    Notamment sur :
    - la possibilité d'utiliser le maximum de types. J'ai spécialisé l'utilisation de char*, mais comment utiliser une liste de int* par exemple ?
    - comment mettre en place le constructeur de recopie et d'affectation de la classe CChainedList ?
    - quel le retour préférable pour les fonctions GetTail() et GetHead() ?

    PS : j'ai utilisé <typename T> au lieu de <class T>, parce que je trouve ça + lisible.

    CODE :
    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
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
     
     
    #if !defined(CHAINEDLIST_H)
    #define CHAINEDLIST_H
     
    #include <iostream>
    using namespace std;
     
     
     
    //Pour que CMaillon connaisse CChainedList
    template <typename T> class CChainedList;
     
    //Classe CMaillon 
    //
    // --> un maillon de notre liste chaînée
    //
    template <typename T>
    class CMaillon
    {
    	//Déclaration d'amitié : toute instance du patron CChainedList est amie du patron CMaillon
    	friend class CChainedList<T>;
     
    private:
    	T m_info;
    	CMaillon* m_suiv;
     
    public:
    	//Constructeur inline pour les types simples (char, int, double ...)
    	CMaillon(T x, CMaillon* s){						
    		m_info = x;
    		m_suiv = s;
    		cout << "CMaillon<T> : dans le constructeur " << this << endl;
    	}
     
    	//destructeur inline
    	~CMaillon(){									
    		cout << "CMaillon<T> : dans le destructeur " << this << endl;
    	}
     
     
    	//Constructeur de recopie inline
    	CMaillon(const CMaillon& x){					
    		m_info = x.m_info;
    		m_suiv = x.m_suiv;
    		cout << "CMaillon<T> : dans le constructeur de recopie " << this << endl;
    	}
     
    	//Operateur d'affectation inline
    	CMaillon& operator = (const CMaillon& x){		
    		if(this != &x)
    		{
    			m_info = x.m_info;
    			m_suiv = x.m_suiv;
    		}
    		cout << "CMaillon<T> : dans l'operateur d'affectation " << this << endl;
    		return *this;
    	}
    };
     
     
    //Constructeur spécialisé pour le type char*
    CMaillon<char*>::CMaillon(char* x, CMaillon* s){		
    	m_info = new char[strlen(x)+1];
    	strcpy(m_info, x);
    	m_suiv = s;
    	cout << "CMaillon<char*> : dans le constructeur " << m_info << " " << this << endl;
    }
     
    //Destructeur spécialisé pour le type char*
    CMaillon<char*>::~CMaillon(){							
    	cout << "CMaillon<char*> : dans le destructeur " << m_info << " " << this << endl;
    	delete m_info;
    }
     
    //Constructeur de recopie spécialisé pour le type char*
    CMaillon<char*>::CMaillon(const CMaillon& x){			
    	delete m_info;
    	m_info = new char[strlen(x.m_info)+1];
    	strcpy(m_info, x.m_info);
    	m_suiv = x.m_suiv;
    	cout << "CMaillon<char*> : dans le constructeur de recopie" << endl;
    }
     
    //Operateur d'affectation spécialisé pour le type char*
    CMaillon<char*>& CMaillon<char*>::operator = (const CMaillon& x){	
    	if(this != &x)
    	{
    		delete m_info;
    		m_info = new char[strlen(x.m_info)+1];
    		strcpy(m_info, x.m_info);
    		m_suiv = x.m_suiv;
    	}
    	cout << "CMaillon<char*> : dans l'operateur d'affectation" << endl;
    	return *this;
    }
     
     
     
    //Classe CChainedList utilisé aussi bien en Queue qu'en Pile
    //
    //		Queue : ajoute en fin (AddTail)
    //				sort en tête (RemoveHead)
    //
    //		Pile :	ajoute en tête (AddHead)
    //				sort en tête (RemoveHead)
    //
    //		Autre : ben comme on veut ...
    //
    //
    template <typename T>
    class CChainedList
    {
    private :
    	CMaillon<T> * m_premier;
    	CMaillon<T> * m_dernier;
    	int m_count;
    	int m_length;
     
    public:
    	//Constructeur
    	CChainedList(int len=1000);
     
    	//Destructeur
    	~CChainedList();
     
    	//Constructeur de recopie --> NON IMPLEMENTE
    	//CChainedList(const CChainedList& ch);
     
    	//Opérateur d'affectation --> NON IMPLEMENTE
    	//CChainedList& operator= (const CChainedList& ch);
     
    	//Teste si Liste vide
    	bool IsEmpty();
     
    	//Renvoie le nombre d'éléments de la liste
    	int GetCount();
     
    	//Ajoute un élément en tête de liste
    	void AddHead(const T& x);
     
    	//Ajoute un élément en fin de liste
    	void AddTail(const T& x);
     
    	//Récupère un pointeur sur l'info de l'élément en tête
    	T* GetHead();
     
    	//Récupère un pointeur sur l'info de l'élément en fin de liste
    	T* GetTail();
     
    	//Sort l'élément en tête
    	void RemoveHead();
     
    	//Sort l'élément en fin
    	void RemoveTail();
     
    	//Sort tous les éléments
    	void RemoveAll();
    };
     
    //Constructeur
    template <typename T>
    CChainedList<T>::CChainedList(int len) {
    		m_premier = NULL;
    		m_dernier = NULL;
    		m_count = 0;
    		m_length = len;
    	cout << "CChainedList<T> : dans le constructeur " << this << endl;
     
    }
     
    //Destructeur
    template <typename T>
    CChainedList<T>::~CChainedList(){
    	RemoveAll();
    	cout << "CChainedList<T> : dans le destructeur " << this << endl;
    }
     
    //Teste si Liste vide
    template <typename T>
    bool CChainedList<T>::IsEmpty() {
    	return (bool)(m_premier == NULL);
    }
     
    //Renvoie le nombre d'éléments de la liste
    template <typename T>
    int CChainedList<T>::GetCount() {
    	return m_count;
    }
     
    //Ajoute un élément en tête de liste
    template <typename T>
    void CChainedList<T>::AddHead(const T& x) {
    	CMaillon<T>* pm = new CMaillon<T>(x, m_premier);
     
    	if(m_premier == NULL)
    		m_dernier = pm;
     
    	m_premier = pm;
    	m_count ++;
    }
     
    //Ajoute un élément en fin de liste
    template <typename T>
    void CChainedList<T>::AddTail(const T& x) {
    	CMaillon<T>* pm = new CMaillon<T>(x, NULL);
    	if(m_premier == NULL)
    		m_premier = pm;
    	else
    		m_dernier->m_suiv = pm;
     
    	m_dernier = pm;
    	m_count ++;
    }
     
    //Récupère l'info de l'élément en tête
    template <typename T>
    T* CChainedList<T>::GetHead(){
    	return m_premier ? &m_premier->m_info : NULL;
    }
     
    //Récupère l'info de l'élément en fin
    template <typename T>
    T* CChainedList<T>::GetTail(){
    	return m_dernier ? &m_dernier->m_info : NULL;
    }
     
    //Sort l'élément en tête
    template <typename T>
    void CChainedList<T>::RemoveHead(){		
    	CMaillon<T>* pm = m_premier;
    	if(pm == NULL)
    		return;
     
    	m_premier = m_premier->m_suiv;
    	delete pm;
    	pm = NULL;
    	m_count --;
    }
     
    //Sort l'élément en fin
    template <typename T>
    void CChainedList<T>::RemoveTail(){		
    	if(IsEmpty() == true)	//cas liste vide
    		return;
     
    	//On n'a pas de pointeur sur l'avant dernier, donc on parcourt toute la liste ...
    	CMaillon<T>* pm = m_premier;
    	CMaillon<T>* pm2 = NULL;
     
    	while(pm->m_suiv != NULL)
    	{
    		pm2 = pm;
    		pm = pm->m_suiv;
    	}
     
    	delete pm;
    	pm = NULL;
     
    	pm2->m_suiv = NULL;
     
    	m_count --;
    }
     
    //Sort tous les éléments
    template <typename T>
    void CChainedList<T>::RemoveAll() {
    	while(!IsEmpty())
    		RemoveHead();
    }
     
    /////////////////////////////////////////////////
     
    #endif
    Merci par avance,
    @+
    Fichiers attachés Fichiers attachés

  2. #2
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    	CMaillon(const CMaillon& x){					
    		m_info = x.m_info;
    		m_suiv = x.m_suiv;
    		cout << "CMaillon<T> : dans le constructeur de recopie " << this << endl;
    	}
    Construit m_info puis y assigne x.m_info (ce qui nécessite donc que ton type soit Assignable et DefaultConstructible)
    Il serait bien mieux de copier directement x.m_info dans m_info.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    CMaillon<char*>::CMaillon(const CMaillon& x){			
    	delete m_info;
    	m_info = new char[strlen(x.m_info)+1];
    	strcpy(m_info, x.m_info);
    	m_suiv = x.m_suiv;
    	cout << "CMaillon<char*> : dans le constructeur de recopie" << endl;
    }
    Le delete ne sert ici qu'à tout faire exploser.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    	CMaillon& operator = (const CMaillon& x){		
    		if(this != &x)
    		{
    			m_info = x.m_info;
    			m_suiv = x.m_suiv;
    		}
    		cout << "CMaillon<T> : dans l'operateur d'affectation " << this << endl;
    		return *this;
    	}
    Le test pour l'auto-affectation est inutile.
    Et en quoi c'est "inline" ?

    A part ça, m_len sert à rien, aucune gestion de const, et la spécialisation pour char* n'est pas pertinente du tout.

    - quel le retour préférable pour les fonctions GetTail() et GetHead() ?
    Des itérateurs, comme dans le type de liste chaînée standard.

  3. #3
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 287
    Par défaut
    * Très bien la séparation liste/maillon

    * pas de iostream dans un .h -- à virer en même temps que les traces ; dans des cas pareils, une fois que tu as déterminé quelle fonction est appellée quand, des tests unitaires sont tout aussi bien, si ce n'est préférables

    * Le fait que les maillons soient copiables ne me parait pas pertinent

    * Tout comme loufoque pour ce qu'il a dit

    * Tes accesseurs (size, ...) seraient mieux en "const"
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  4. #4
    Membre éclairé
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    577
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 577
    Par défaut
    Bonjour,

    Déjà merci beaucoup pour tous vos conseils

    Citation Envoyé par loufoque
    Construit m_info puis y assigne x.m_info (ce qui nécessite donc que ton type soit Assignable et DefaultConstructible)
    Il serait bien mieux de copier directement x.m_info dans m_info.
    Exact, c'est corrigé. J'y avais pas pensé.

    Citation Envoyé par loufoque
    Le delete ne sert ici qu'à tout faire exploser.
    Désolé, ça vient d'un copier/coller de celui d'affectation, qui consiste à netoyer et recréer les parties dynamiques. Merci.

    Citation Envoyé par loufoque
    Le test pour l'auto-affectation est inutile.
    Pourquoi ? Est-ce que c'est juste parce qu'il n'y a pas de partie dynamique, et donc que l'on peut toujours faire :
    m_info = x.m_info;
    m_suiv=x.m_suiv; ?
    Disons que c'était juste une précaution pour pas faire un truc qui servait à rien ...
    Par conte, je viens de penser que ça signifie que si j'utilise un objet de classe comme type de m_info, il faudra qu'il y ait de définit un opérateur d'affectation pour lui ...

    Citation Envoyé par loufoque
    Et en quoi c'est "inline" ?
    Ben quand on fournit directement la définition d'une fonction membre dans la déclaration même de la classe, elle est "inline".
    Je le précisais juste dans le commentaire ...

    Citation Envoyé par loufoque
    A part ça, m_len sert à rien
    Ouais c'est vrai, je venais de le rajouter ... Je vais le prendre en compte maintenant ...

    Citation Envoyé par loufoque
    aucune gestion de const
    Il vallait mieux en mettre où des const ?

    Citation Envoyé par loufoque
    et la spécialisation pour char* n'est pas pertinente du tout.
    Et bien c'est justement ça ... Comment la rendre + pertinente ou + générique ? Peut-on mieux s'y prendre ?


    Citation Envoyé par loufoque
    Des itérateurs, comme dans le type de liste chaînée standard.
    Ouais ... Bon, on va voir ça maintenant

    Merci
    @+

  5. #5
    Membre éclairé
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    577
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 577
    Par défaut
    Bonjour,

    Citation Envoyé par Luc Hermitte

    * pas de iostream dans un .h -- à virer en même temps que les traces ; dans des cas pareils, une fois que tu as déterminé quelle fonction est appellée quand, des tests unitaires sont tout aussi bien, si ce n'est préférables

    ---> OK, je vire.


    * Le fait que les maillons soient copiables ne me parait pas pertinent

    ---> Effectivement, je ne m'en sers pas ... mais c'était pour avoir une classe canonique.



    * Tes accesseurs (size, ...) seraient mieux en "const"

    --> Si ça rejoint ce que dit Loufoque la-desssus, pourriez-vous m'expliquer ce qu'il est préférable de faire avec des const, parce que là je vois pas ...

    ha, j'ai aussi corrigé les delete des m_info (pour les char*):
    crochets puisqu'on alloue un tableau ...

    @+

  6. #6
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Par défaut
    Exact, c'est corrigé. J'y avais pas pensé.
    Tu sais comment on fait au moins ? C'est avec les listes d'initialisations du constructeur (que tu devrais utiliser autant que possible)

    Et bien c'est justement ça ... Comment la rendre + pertinente ou + générique ? Peut-on mieux s'y prendre ?
    Utiliser un type "string" (comme std::string) à la place de char*.
    Et du coup il n'y a aucune spécialisation à faire.

  7. #7
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 287
    Par défaut
    La "canonicité" ne doit pas être une obligation utilisée de manière aveugle. Quand la copiabilité n'a pas de sens, on l'interdit explicitement.

    Je laisserais vraiment tomber les char*.

    Pour le const =>
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    struct List {
        ....
        int size() const;
        ...
    };
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  8. #8
    Membre éclairé
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    577
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 577
    Par défaut
    Citation Envoyé par loufoque
    Tu sais comment on fait au moins ? C'est avec les listes d'initialisations du constructeur (que tu devrais utiliser autant que possible)

    ---> Si je dis "c'est corrigé", c'est que "c'est corrigé" ... Donc oui je sais comment on fait, et il vaut mieux d'ailleurs.
    Bien sûr que j'utilise les listes d'initialisation du constructeur ... le truc, c'est que malgré moi je trouve vraiment pas ça lisible du tout, et que en codant vite j'initialise "séquentiellement" dans la déclaration du constructeur.


    Utiliser un type "string" (comme std::string) à la place de char*.
    Et du coup il n'y a aucune spécialisation à faire.

    ---> Oui je suis d'accord, ou un CString si on est en MFC, le truc c'est qu'il y a pas mal de gars qui connaissent que les char* ...
    Mais bon au-delà de ça, immaginons qu'on veut stocker des int* (je sais c'est con, mais c'est pour prendre un cas qui rentre pas dans le template general),
    qu'est ce qu'il vaut mieux faire ?
    un template CMaillon<T*> ?
    Merci beaucoup de vos remarques

  9. #9
    Membre éclairé
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    577
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 577
    Par défaut
    Citation Envoyé par Luc Hermitte
    La "canonicité" ne doit pas être une obligation utilisée de manière aveugle. Quand la copiabilité n'a pas de sens, on l'interdit explicitement.
    ---> OK, ben alors il me suffit de passer en privés le constructeur de recopie et l'opérateur d'affectation.
    Je garde une forme canonique comme ça


    Citation Envoyé par Luc Hermitte
    Je laisserais vraiment tomber les char*.
    --> Oui je sais tous ceux qui font du C++ veulent pas des char* ...
    mais y a des gens qui sont perdus sans ça.


    Citation Envoyé par Luc Hermitte
    Pour le const =>
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    struct List {
        ....
        int size() const;
        ...
    };
    ---> Quel est la gravité de ne pas prendre en compte le const ? Parce que par exemple,ma fonction CChainedList<T>::GetCount(), je sais qu'elle va pas me l'abîmer mon objet ...
    C'est au niveau rigueur qu'il faut l'utiliser ??

    @+

  10. #10
    Membre Expert
    Avatar de Eusebe
    Inscrit en
    Mars 2006
    Messages
    1 992
    Détails du profil
    Informations personnelles :
    Âge : 47

    Informations forums :
    Inscription : Mars 2006
    Messages : 1 992
    Par défaut
    Citation Envoyé par olive_le_malin
    ---> Quel est la gravité de ne pas prendre en compte le const ? Parce que par exemple,ma fonction CChainedList<T>::GetCount(), je sais qu'elle va pas me l'abîmer mon objet ...
    C'est au niveau rigueur qu'il faut l'utiliser ??
    Ca te permettra d'utiliser ces méthodes sur des objets constants.

  11. #11
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Par défaut
    le truc c'est qu'il y a pas mal de gars qui connaissent que les char*
    Que ces gens apprennent les bases du C++ ou qu'ils programment en C.

    Mais bon au-delà de ça, immaginons qu'on veut stocker des int* (je sais c'est con, mais c'est pour prendre un cas qui rentre pas dans le template general),
    qu'est ce qu'il vaut mieux faire ?
    un template CMaillon<T*> ?
    Il n'y a rien à faire.
    Le cas général convient très bien pour stocker une liste de pointeurs vers des int.

  12. #12
    Membre éclairé
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    577
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 577
    Par défaut
    Bonjour,
    et merci à vous 3 pour vos remarques...

    En en tenant compte, voilà donc ce que ça dnne

    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
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
     
     
     
    #if !defined(CHAINEDLIST_H)
    #define CHAINEDLIST_H
     
    #include <iostream>
     
    using namespace std;
     
     
    //Pour que CMaillon connaisse CChainedList
    template <typename T> class CChainedList;
     
    //Classe CMaillon 
    //
    // --> un maillon de notre liste chaînée
    //
    template <typename T>
    class CMaillon
    {
    	//Déclaration d'amitié : toute instance du patron CChainedList est amie du patron CMaillon
    	friend class CChainedList<T>;
     
    private:
    	T m_info;
    	CMaillon* m_suiv;
     
    public:
    	//Constructeur inline pour les types simples (char, int, double ...)
    	CMaillon(T x, CMaillon* s) : m_info(x) {						
    		m_suiv = s;
    		cout << "CMaillon<T> : dans le constructeur " << this << endl;
    	}
     
    	//destructeur inline
    	~CMaillon(){									
    		cout << "CMaillon<T> : dans le destructeur " << this << endl;
    	}
     
    private :
    	//Constructeur de recopie inline --> on définit explicitement qu'on veut pas qu'il soit utilisé
    	CMaillon(const CMaillon& x) : m_info(x.m_info) {	
    		m_suiv = x.m_suiv;
    		cout << "CMaillon<T> : dans le constructeur de recopie " << this << endl;
    	}
     
    	//Operateur d'affectation inline --> on définit explicitement qu'on veut pas qu'il soit utilisé
    	CMaillon& operator = (const CMaillon& x) {
    		m_info = x.m_info;			
    		m_suiv = x.m_suiv;
    		cout << "CMaillon<T> : dans l'operateur d'affectation " << this << endl;
    		return *this;
    	}
     
     
    };
     
     
    /*
    //Constructeur spécialisé pour le type char*
    CMaillon<char*>::CMaillon(char* x, CMaillon* s){		
    	m_info = new char[strlen(x)+1];
    	strcpy(m_info, x);
    	m_suiv = s;
    	cout << "CMaillon<char*> : dans le constructeur " << m_info << " " << this << endl;
    }
     
    //Destructeur spécialisé pour le type char*
    CMaillon<char*>::~CMaillon(){							
    	cout << "CMaillon<char*> : dans le destructeur " << m_info << " " << this << endl;
    	delete [] m_info;
    }
     
    //Constructeur de recopie spécialisé pour le type char*
    CMaillon<char*>::CMaillon(const CMaillon& x){			
    	//delete m_info;
    	m_info = new char[strlen(x.m_info)+1];
    	strcpy(m_info, x.m_info);
    	m_suiv = x.m_suiv;
    	cout << "CMaillon<char*> : dans le constructeur de recopie" << endl;
    }
     
    //Operateur d'affectation spécialisé pour le type char*
    CMaillon<char*>& CMaillon<char*>::operator = (const CMaillon& x){	
    	if(this != &x)
    	{
    		delete [] m_info;
    		m_info = new char[strlen(x.m_info)+1];
    		strcpy(m_info, x.m_info);
    		m_suiv = x.m_suiv;
    	}
    	cout << "CMaillon<char*> : dans l'operateur d'affectation" << endl;
    	return *this;
    }
    */
     
     
    //Classe CChainedList utilisé aussi bien en Queue qu'en Pile
    //
    //		Queue : ajoute en fin (AddTail)
    //				sort en tête (RemoveHead)
    //
    //		Pile :	ajoute en tête (AddHead)
    //				sort en tête (RemoveHead)
    //
    //		Autre : ben comme on veut ...
    //
    //
    template <typename T>
    class CChainedList
    {
    private :
    	CMaillon<T> * m_premier;
    	CMaillon<T> * m_dernier;
    	unsigned int m_count;
    	unsigned int m_maxLen;
    	static const unsigned int m_infLen;
     
    public:
    	//Constructeur
    	CChainedList(int len = m_infLen);	//par défaut taille infinie
     
    	//Destructeur
    	~CChainedList();
     
    	//Constructeur de recopie --> NON IMPLEMENTE
    	//CChainedList(const CChainedList& ch);
     
    	//Opérateur d'affectation --> NON IMPLEMENTE
    	//CChainedList& operator= (const CChainedList& ch);
     
    	//Teste si Liste vide
    	bool IsEmpty() const;
     
    	//Renvoie le nombre d'éléments de la liste
    	unsigned int GetCount() const;
     
    	//Ajoute un élément en tête de liste
    	int AddHead(const T& x);
     
    	//Ajoute un élément en fin de liste
    	int AddTail(const T& x);
     
    	//Récupère un pointeur sur l'info de l'élément en tête
    	T& GetHead();
     
    	//Récupère un pointeur sur l'info de l'élément en fin de liste
    	T& GetTail();
     
    	//Sort l'élément en tête
    	void RemoveHead();
     
    	//Sort l'élément en fin
    	void RemoveTail();
     
    	//Sort tous les éléments
    	void RemoveAll();
    };
     
    //Init membre static
    template <typename T>
    const unsigned int CChainedList<T>::m_infLen = -1;	//ce qui fait 2^32 - 1 = 0xffffffff
     
     
    //Constructeur
    template <typename T>
    CChainedList<T>::CChainedList(int len) : m_count(0), m_maxLen(len) {
    		m_premier = NULL;
    		m_dernier = NULL;
    	cout << "CChainedList<T> : dans le constructeur " << this << endl;
     
    }
     
    //Destructeur
    template <typename T>
    CChainedList<T>::~CChainedList(){
    	RemoveAll();
    	cout << "CChainedList<T> : dans le destructeur " << this << endl;
    }
     
    //Teste si Liste vide
    template <typename T>
    bool CChainedList<T>::IsEmpty() const {
    	return (bool)(m_premier == NULL);
    }
     
    //Renvoie le nombre d'éléments de la liste
    template <typename T>
    unsigned int CChainedList<T>::GetCount() const {
    	return m_count;
    }
     
    //Ajoute un élément en tête de liste
    template <typename T>
    int CChainedList<T>::AddHead(const T& x) {
     
    	if(m_count >= m_maxLen)
    		return 0;
     
    	CMaillon<T>* pm = new CMaillon<T>(x, m_premier);
     
    	if(m_premier == NULL)
    		m_dernier = pm;
     
    	m_premier = pm;
    	m_count ++;
     
    	return 1;
    }
     
    //Ajoute un élément en fin de liste
    template <typename T>
    int CChainedList<T>::AddTail(const T& x) {
     
    	if(m_count >= m_maxLen)
    		return 0;
     
    	CMaillon<T>* pm = new CMaillon<T>(x, NULL);
    	if(m_premier == NULL)
    		m_premier = pm;
    	else
    		m_dernier->m_suiv = pm;
     
    	m_dernier = pm;
    	m_count ++;
     
    	return 1;
    }
     
    //Récupère l'info de l'élément en tête.
    //Attention : liste doit être non-vide
    template <typename T>
    T& CChainedList<T>::GetHead(){
    	//return m_premier ? m_premier->m_info : NULL;
    	return m_premier->m_info;
    }
     
    //Récupère l'info de l'élément en fin.
    //Attention : liste doit être non-vide
    template <typename T>
    T& CChainedList<T>::GetTail(){
    	return m_dernier->m_info;
    }
     
    //Sort l'élément en tête
    template <typename T>
    void CChainedList<T>::RemoveHead(){		
    	CMaillon<T>* pm = m_premier;
    	if(pm == NULL)
    		return;
     
    	m_premier = m_premier->m_suiv;
    	delete pm;
    	pm = NULL;
    	m_count --;
    }
     
    //Sort l'élément en fin
    template <typename T>
    void CChainedList<T>::RemoveTail(){		
    	if(IsEmpty() == true)	//cas liste vide
    		return;
     
    	//On n'a pas de pointeur sur l'avant dernier, donc on parcourt toute la liste ...
    	CMaillon<T>* pm = m_premier;
    	CMaillon<T>* pm2 = NULL;
     
    	while(pm->m_suiv != NULL)
    	{
    		pm2 = pm;
    		pm = pm->m_suiv;
    	}
     
    	delete pm;
    	pm = NULL;
     
    	pm2->m_suiv = NULL;
     
    	m_count --;
    }
     
    //Sort tous les éléments
    template <typename T>
    void CChainedList<T>::RemoveAll() {
    	while(!IsEmpty())
    		RemoveHead();
    }
     
    /////////////////////////////////////////////////
     
    #endif
    Ps: j'ai pas encore viré les traces ...

    Par contre :


    Citation:
    Mais bon au-delà de ça, immaginons qu'on veut stocker des int* (je sais c'est con, mais c'est pour prendre un cas qui rentre pas dans le template general),
    qu'est ce qu'il vaut mieux faire ?
    un template CMaillon<T*> ?

    Il n'y a rien à faire.
    Le cas général convient très bien pour stocker une liste de pointeurs vers des int.
    Là je vois pas, parce ce que que ce soit des char* ou des int* ou des pinpin*, si on alloue pas dynamiquement une zone de stockage, ben ça va faire BOUM ...
    Désolé si j'ai pas compris ta remarque.

    @+

  13. #13
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Par défaut
    Exemple avec les conteneurs standard (désolé j'ai pas envie de me pencher sur ton cas)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <list>
     
    int foo = 5;
     
    int main()
    {
        std::list<int*> list;
        list.push_back(new int(3));
        list.push_back(&foo);
    }
    Bien sûr dans cet exemple stupide il y a une jolie fuite mémoire...

  14. #14
    Membre éclairé
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    577
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 577
    Par défaut
    oui
    complètement d'accord avec toi ...
    Enfin, un petit bémol quand même :

    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
     
    #include <list>
     
    int foo = 5;
    list<int*> list2;
     
    void test()
    {
    	int refoo[8] = {0,1,2,3,4,5,6,7};
        list2.push_back(refoo);
     
    	int* result = list2.front();  //Ici mon result contient {0,1,2,3,4,5,6,7}
     
    	return;
    }
     
    int main()
    {
        std::list<int*> list;
        list.push_back(new int(3));
        list.push_back(&foo);
     
    	test();
    	Sleep(1000);
     
    	//Ben j'aimerais bien voir ce que ontient list2 maintenant ?
    	int* result = list2.front();	//Ici mon result contient n'importe quoi, ce qui est tout à fait normal !!
     
    	return 0;
    }
    Donc cela ne peut marcher que si les onjets sont utilisés localement, ou alloués dynamiquement.

    C'est pour cela que je voulais passer par une spécialisation de la classe pour les pointeurs, qui permettait d'allouer un espace, ce que ne permet pas e template de base.

    @+

  15. #15
    Membre Expert
    Avatar de Eusebe
    Inscrit en
    Mars 2006
    Messages
    1 992
    Détails du profil
    Informations personnelles :
    Âge : 47

    Informations forums :
    Inscription : Mars 2006
    Messages : 1 992
    Par défaut
    Si on utilise des pointeurs dans des conteneurs, c'est en général que l'on ne veut pas que les objets contenus soient copiés, non ? (ou alors qu'ils ne le peuvent pas)

    Et pour éviter ton problème de stockage d'un tableau, il suffit de stocker un vector d'int...

  16. #16
    Membre éclairé
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    577
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 577
    Par défaut
    oui et donc de passer par un objet ...
    comme pour les char*, et passer par un std::string ...

    Ce que je voulais dire, c'est que l'on compte sur le fait que le gars qui va l'utiliser fasse ce qu'on a supposé qu'il allait faire. S'il dévie un peu, comme dans mon exemple, BOUM !!

    @+

  17. #17
    Membre Expert
    Avatar de Eusebe
    Inscrit en
    Mars 2006
    Messages
    1 992
    Détails du profil
    Informations personnelles :
    Âge : 47

    Informations forums :
    Inscription : Mars 2006
    Messages : 1 992
    Par défaut
    Mais si tu fais ça, tu empêche d'utiliser des classes non copiables, ou encore d'utiliser le polymorphisme...

  18. #18
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 287
    Par défaut
    Je viens de percuter un truc.
    Ta classe, ce n'est pas un exo. En exo, c'est intéressant. En production, c'est un suicide à moyen termes.

    Si vous vous amusez à tout réinventer pour des personnes qui refusent d'apprendre le C++, vous n'êtes pas sortis de l'auberge. Bonjour les débuggages et autres pertes de temps.

    Vous n'avez pas la "chance" d'avoir des règles qualités imposées par le client qui demandent à utiliser en priorité la bibliothèque standard du C++ ?

    Accessoirement, si tu veux une classe unique qui s'adapte à ce quelle reçoit (genre distinguer une liste de tableaux d'une liste d'éléments), il va falloir sortir des artifices moyennement complexes, comme ceux à base de polices p.ex.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  19. #19
    Membre éclairé
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    577
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 577
    Par défaut
    Citation Envoyé par Luc Hermitte
    Je viens de percuter un truc.
    Ta classe, ce n'est pas un exo. En exo, c'est intéressant. En production, c'est un suicide à moyen termes.

    Si vous vous amusez à tout réinventer pour des personnes qui refusent d'apprendre le C++, vous n'êtes pas sortis de l'auberge. Bonjour les débuggages et autres pertes de temps.

    Vous n'avez pas la "chance" d'avoir des règles qualités imposées par le client qui demandent à utiliser en priorité la bibliothèque standard du C++ ?

    Accessoirement, si tu veux une classe unique qui s'adapte à ce quelle reçoit (genre distinguer une liste de tableaux d'une liste d'éléments), il va falloir sortir des artifices moyennement complexes, comme ceux à base de polices p.ex.
    disons qu'à la base c'était un exo pour moi, pour me familiariser avec les template ... et j'essayais de raisonner au maximum général, et aussi de voir et de comprendre les contraintes que cela engendrait.
    Mais effectivement, ça devient beaucoup + compliqué ...

Discussions similaires

  1. Réponses: 1
    Dernier message: 28/09/2006, 09h55
  2. [MySQL] Réaliser un script de statistiques : vos conseils pour l'architecture de la table ?
    Par MaTHieU_ dans le forum PHP & Base de données
    Réponses: 9
    Dernier message: 26/08/2006, 00h46
  3. Trés urgent (démission), besoin de vos conseils
    Par recttamuni dans le forum Démission
    Réponses: 9
    Dernier message: 25/07/2006, 21h49
  4. Applet au contenu dynamique, vos conseils...
    Par yizashi dans le forum Applets
    Réponses: 1
    Dernier message: 05/04/2006, 22h41
  5. Réponses: 10
    Dernier message: 31/12/2005, 20h10

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