Partage
  • Partager sur Facebook
  • Partager sur Twitter

Template de Class

Sujet résolu
    3 octobre 2020 à 16:04:13

    Bonjour,

    J'ai deux fonction identique mais qui proviennent de class différentes. J'aimerai en faire plus qu'une seule en utilisant les template. Malheureusement je n'y arrive pas.

    PS: La fonction est un simple tri à bulle.

    Voici le code :

    class Etudiant {
    public:
    	int Id;
    	string nom;
    
    	void triNom(Etudiant T[], int& n);
    	
    	// fonctions à ajouter 
    
    };
    
    class PROF {
    public:
    	int Id;
    	string nom, Service;
    	
    	void triNom(PROF T[], int& n);
    
    	// fonctions à ajouter
    
    };
    void Etudiant::triNom(Etudiant tab[], int& N) {
    	int i, perm = 1;
    	Etudiant tmp;
    	while (perm == 1) {
    		perm = 0;
    		for (i = 0; i < N - 1; i++) {
    			if (tab[i].nom > tab[i + 1].nom) {
    				tmp = tab[i];
    				tab[i] = tab[i + 1];
    				tab[i + 1] = tmp;
    				perm = 1;
    			}
    		}
    	}
    }
    void PROF::triNom(PROF tab[], int& N) {
    	int i, perm = 1;
    	PROF tmp;
    	while (perm == 1) {
    		perm = 0;
    		for (i = 0; i < N - 1; i++) {
    			if (tab[i].nom > tab[i + 1].nom) {
    				tmp = tab[i];
    				tab[i] = tab[i + 1];
    				tab[i + 1] = tmp;
    				perm = 1;
    			}
    		}
    	}
    }




    • Partager sur Facebook
    • Partager sur Twitter
      3 octobre 2020 à 16:55:29

      Ce que tu cherches à faire ressemble plus à de l'héritage qu'a de la généricité

      Est-tu sûr de vouloir utiliser les template ?

      • Partager sur Facebook
      • Partager sur Twitter
        3 octobre 2020 à 17:47:03

        C'est bien de la généricité, mais mettre la fonction de tri en membre n'a aucun intérêt. En plus, en étant membre, elle devrait être statique. Et le paramètre N en référence n'a aucun sens.

        Le mieux est de faire une fonction libre, comme tous les algorithmes de la stl, dont std::sort.

        • Partager sur Facebook
        • Partager sur Twitter
          3 octobre 2020 à 17:47:22

          Dans l'énoncé de mon TP il m'est dit cela : "Vous devez absolument optimiser votre code (template de fonctions) afin de permettre la réutilisation des morceaux de code."

          J'ai déjà mit des template pour mes fonctions classiques mais ici avec les class je ne sais pas trop comment faire.

          • Partager sur Facebook
          • Partager sur Twitter
            3 octobre 2020 à 18:55:44

            cmdevo a écrit:

            Ce que tu cherches à faire ressemble plus à de l'héritage qu'a de la généricité

            Est-tu sûr de vouloir utiliser les template ?


            Non, surement pas...

            On ne peut décemment pas considérer qu'un professeur soit un élève ou qu'un élève puisse être un professeur... La notion d'héritage ne fonctionnera donc absolument pas, étant donné que les deux classes ont des invariant trop différents (malgré le fait qu'elle présentent toutes deux des fonctions sommes toutes semblables).

            Arthurd'Hertog a écrit:

            Bonjour,

            J'ai deux fonction identique mais qui proviennent de class différentes. J'aimerai en faire plus qu'une seule en utilisant les template. Malheureusement je n'y arrive pas.

            PS: La fonction est un simple tri à bulle.

            Voici le code :

            class Etudiant {
            public:
            	int Id;
            	string nom;
            
            	void triNom(Etudiant T[], int& n);
            	
            	// fonctions à ajouter 
            
            };
            
            class PROF {
            public:
            	int Id;
            	string nom, Service;
            	
            	void triNom(PROF T[], int& n);
            
            	// fonctions à ajouter
            
            };
            void Etudiant::triNom(Etudiant tab[], int& N) {
            	int i, perm = 1;
            	Etudiant tmp;
            	while (perm == 1) {
            		perm = 0;
            		for (i = 0; i < N - 1; i++) {
            			if (tab[i].nom > tab[i + 1].nom) {
            				tmp = tab[i];
            				tab[i] = tab[i + 1];
            				tab[i + 1] = tmp;
            				perm = 1;
            			}
            		}
            	}
            }
            void PROF::triNom(PROF tab[], int& N) {
            	int i, perm = 1;
            	PROF tmp;
            	while (perm == 1) {
            		perm = 0;
            		for (i = 0; i < N - 1; i++) {
            			if (tab[i].nom > tab[i + 1].nom) {
            				tmp = tab[i];
            				tab[i] = tab[i + 1];
            				tab[i + 1] = tmp;
            				perm = 1;
            			}
            		}
            	}
            }

            Attention! La fonction de tri d'un tableau (d'élèves ou de professeurs) ne devrait pas être une fonction membre de tes classes Etudiants et Profs, mais devrait être une fonction libre!

            De plus, c'est quoi cette idée de travailler avec des tableaux C style???  ne t'a-t-on pas appris à utiliser std::vector lorsque tu dois manipuler un nombre d'élément qui ne peut être connu qu'à l'exécution ?

            Enfin, le problème n'est pas le tri proprement dit, car la bibliothèque nous offre une fonction qui fera cela pour nous (std::sort). Le véritable problème va surtout consister à se mettre d'accord sur le critère de tri qu'il faudra utiliser.

            En effet, pour qu'un tri puisse être exécuté, il faut impérativement disposer d'une logique qui permet, en gros, de considérer que l'élément A est "plus petit" que l'élément B, si bien qu'il faut  commencer par se mettre d'accord sur le sens que l'on va donner à "est plus  petit que".

            "En temps normal", nous utiliserions sans doute l'opérateur < sous forme de fonction libre pour répondre à la question fatidique de "a est-il plus petit que b?".

            Cependant, ta classe Etudiant dispose de deux champs susceptibles d'être utilisé pour déterminer si un étudiant est "plus petit" qu'un autre: son champs Id et son champs nom.  On peut donc souhaiter trier un ensemble d'étudiants soit en fonction de son identifiant, soit en fonction de son nom.

            De même, ta classe Prof dispose également de ces deux champs, mais elle dispose également d'un champs Service, qui pourrait aussi servir au tri.

            Or, nous pouvons définir qu'une seule et unique logique pour l'opérateur < de chacune de ces classes, ce qui risque  de limiter les possibilités de tri.

            Par chance, nous disposons de différents moyens de contourner cette restriction, par exemple:

            • En créant ce que l'on appelle un prédicat (technique largement répandue avant l'arrivée de C++11)
            • En fournissant une expression lambda pour les différents critères de tri (solution préférable depuis que C++11 est utilisable ;) ).

            L'idée du prédicat est qu'il est tout à fait possible de créer une "fonction objet": une classe ou  une structure qui n'expose qu'une seule fonction répondant à  la question que l'on se pose.

            Nous pourrions, par exemple, écrire deux prédicats pour la classe Eleve sous la forme de

            /* permet le tri sur base du champs Id */
            struct LessById{
                static bool operator() (Eleve const & a, Eleve const & b){
                    return a.Id < b.Id;
                }
            };
            /* permet le tri sur base du champs nom */
            struct LessByName{
            
                static bool operator() (Eleve const & a, Eleve const & b){
                    return a.nom < b.nom;
                }
            };

            Et, comme il s'avère que ces deux prédicats pourraient tout aussi bien fonctionner avec des instances de la classe Prof, nous pourrions les transformer de manière à ce qu'ils acceptent "n'importe quel type de donnée exposant un champs Id (ou un champs nom)" sous la forme de

            /* permet le tri sur base du champs Id */
            struct LessById{
                template <typename T>
                bool operator() (T const & a, T const & b) const{
                    return a.Id < b.Id;
                }
            };
            /* permet le tri sur base du champs nom */
            struct LessByName{
                template <typename T>
                bool operator() (T const & a, T const & b) const {
                    return a.nom < b.nom;
                }
            };

            Il ne nous restera alors plus qu'à traiter le cas du service exposé par la classe Prof, si tant est qu'il y ait du sens à vouloir les trier par service (c'est à toi de voir, on en reparle en cas de besoin :D ).

            Ces deux prédicats pourront sans aucun problème être utilisés pour trier des tableaux de profs ou d'étudiants sous une forme proche de

            std::vector<Etudiant> lesEtudiants;
            /* ... remplissons ce tableau */
            /* trie les étudiants en fonction de leur nom */
            std::sort(lesEtudiants.begin(), lesEtudiants.end(), LessByName{});
            
            
            std::vector<Prof> lesProfs;
            /* ... remplissons ce tableau */
            /* trie les profs en fonction de leur id */
            std::sort(lesProfs.begin(), lesProfs.end(), lessById{});
            

            La deuxième solution, celle qui utilise les expressions lambda, est au moins tout aussi cool, surtout depuis qu'il est possible d'utiliser l'inférence de types pour sélectionner le type des paramètres de ces expressions.

            En gros, depuis C++11, nous pourrions carrément éviter la création des prédicats tels que je te les ai montré plus haut, et nous pourrions tout aussi bien  directement indiquer la logique qui doit être utilisée pour le tri, sous une forme qui pourrait être proche de

            struct Prof{
            	std::string nom;
            	std::string service;
            	int id;
            };
            int
            main ()
            {
            	
            	std::vector<Prof> lesProfs;
            	lesProfs.push_back({"Henri","Math",1});
            	lesProfs.push_back({"Albert","Francais",3});
            	lesProfs.push_back({"Zoe","Histoire",2});
            	lesProfs.push_back({"Jean","Gym",4});
            	/* trions les profs par nom */
            	std::sort(lesProfs.begin(), lesProfs.end(), [](auto const & a, auto  const & b){
            		 return a.nom <b.nom;
            	 });
            	for(auto const & it: lesProfs){
            		std::cout<<it.id<<" "<<it.nom<<" "<<it.service<<"\n";
            	}
            		/* trions les profs par id */
            	std::sort(lesProfs.begin(), lesProfs.end(), [](auto const & a, auto  const & b){
            		 return a.id < b.id;
            	 });
            	for(auto const & it: lesProfs){
            		std::cout<<it.id<<" "<<it.nom<<" "<<it.service<<"\n";
            	}
            }

            PS: Tu devrais vraiment être beaucoup plus attentif à respecter certaines conventions lorsque tu choisi tes identifiants .  Par exemple, en choisissant "une bonne fois pour toutes" la forme que prendra le nom de tes classes: Soit, c'est la première lettre en majuscule et tout le reste en minuscule (comme ta classe Etudiant), soit c'est tout en majuscule comme ta classe PROF.

            De même, pour les différentes données de tes classes: ou bien tu choisi de leur donner un nom "tout en minuscule" (comme nom), ou bien tu leur donne un nom dont la première lettre est en majuscule (comme Id ou Service).

            Je me fous pas mal du choix que tu feras, car cela ne va pas changer grand chose. Par contre, il me semble indispensable de faire un choix, et de s'y tenir au moins pour le projet sur lequel tu travailles.  Tu verras que cela te simplifiera énormément la vie par la suite ;)

            • Partager sur Facebook
            • Partager sur Twitter
            Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
              4 octobre 2020 à 0:01:28

              Bonjour,

              Le code envoyé  est le code de base dans le TP que j'ai à réaliser et est donc écrit par mon professeur à l'Université. Je n'ai pas encore vu les vector donc on utilise encore les tableaux. Le tri en soit est bon car j'ai plusieurs fonctions de tri en fonction de ce que choisit l'utilisateur ( tri par Id, tri par nom, ect). Ici je ne montre que celui par nom.  Ce que vous me proposez comme solution est "trop complexe" pour mon niveau je pense.Il m'est mit comme indice dans le TP d'utiliser des templates.

              Vous me dites donc que je ne devrais pas mettre la fonction de triNom dans le class mais créer une fonction externe ? Ainsi dans cette fonction je peux y appliquer un template et en avoir qu'une seule ?

              PS : Évidemment le code envoyé ci-dessus n'est pas complet mais c'était juste pour savoir comment "fusionner" ces 2 fonctions fortement similaire en une.

              Merci beaucoup pour vos conseils et j'en prend note.

              • Partager sur Facebook
              • Partager sur Twitter
                4 octobre 2020 à 2:13:27

                Arthurd'Hertog a écrit:

                Bonjour,

                Vous me dites donc que je ne devrais pas mettre la fonction de triNom dans le class mais créer une fonction externe ? Ainsi dans cette fonction je peux y appliquer un template et en avoir qu'une seule ?


                Ce que je te dis surtout, c'est qu'il n'y a que peu de sens à demander au  contenu (à un élève particulier, somme toute) de gérer l'ensemble du contenant dans lequel il se trouve (le tableau d'élèves) dans lequel il se trouve.

                Lorsqu'il est question de trier des éléments, l'idée est toujours de demander au contenant de trier le contenu, jamais l'inverse ;)

                Tu peux, éventuellement, demander à un élément particulier de se comparer (selon un critère bien particulier) à un autre élément pour savoir quelle position donner à l'un et à l'autre (qui arrive en premier et  qui arrive ensuite).

                et faire en sorte que ta classe expose des fonctions de comparaisons d'une manière proche de

                class Eleve{
                public:
                    int id;
                    int nom;
                    bool lessByName( Eleve const & other) const{
                        return nom < other.nom;
                    }
                    bool lessById(Eleve const & other) const{
                        return id < other.id;
                    }
                    /* ... */
                };

                Mais cela pourrait poser quelques problèmes, entre autres, en termes de commutativité.  Et c'est pour cela qu'une fonction libre (éventuellement déclarée comme étant amie, bien que ce ne sera pas nécessaire étant donné que les champs de ta classe Eleve sont publics) sera sans doute beaucoup plus utile, ce qui nous amènera plutôt à un code proche de

                class Eleve{
                public:
                    int id;
                    int nom;
                    /* ... */
                };
                
                    bool lessByName( Eleve const & a, Eleve const & b){
                        return a.nom < b.nom;
                    }
                    bool lessById(Eleve const & a, Eleve const & b) {
                        return a.id < b.id;
                    }

                Arthurd'Hertog a écrit:

                PS : Évidemment le code envoyé ci-dessus n'est pas complet mais c'était juste pour savoir comment "fusionner" ces 2 fonctions fortement similaire en une.

                C'est pas très clair... Que voudrais tu "fusionner"? estce

                1. la fonction qui permet de comparer les id de deux élèves avec la fonction qui permet de comparer les id de deux professeurs?
                2. la fonction qui permet de trier un tableau d'élèves en fonction de leur id avec celle qui permet de trier un tableau d'élèves en fonction de leur nom?
                3. la fonction qui permet de trier un tableau d'élèves (peu importe le critère de tri) avec celle qui permet de trier un tableau de profs selon le même critère de tri?
                4. la fonction qui permet de trier n'importe quel contenu de tableau, peu importe le critère de tri et celle qui permet de trier n'importe quel autre tableau en utilisant n'importe quel autre critère de tri?

                Pour le (1), tu peux, effectivement, envisager de créer une fonction de comparaison template pour chaque critère de comparaison.  Cette fonction pouvant être utilisée avec n'importe quel type exposant un champs bien précis, par exemple

                /* Peu importe le type de la donnée comparée, 
                 * ce type doit disposer d'un champs nommé id qui
                 * puisse être comparé par "est plus petit que"
                 */
                template <typename T>
                bool lessById(T const & a, T const & b){
                    return a.id < b.id;
                }

                le (2) est une fusion impossible, ou peu s'en faut...

                On pourrait, bien sur, commencer à jouer avec des politiques et des traits de politique pour arriver, au final, à regrouper le tout, mais cela ne ferait que rajouter énormément de complexité par rapport au simple fait de créer une fonction qui permet la comparaison de deux élèves (ou de deux données de type T) sur base de leur champs nommé id  et une fonction qui permet la comparaison de deux élèves (ou de deux données de type T) sur base de leur champs nommé nom ;) .

                Pour le (3), à partir du moment où tu dispose d'une fonction permettant la comparaison de deux données de types identiques sur base d'un critère bien particulier, la fonctions std::sort te permettra de trier n'importe quelle collection de ce type de donnée bien particulier sur base du critère de tri indiqué.

                pour le (4) c'est exactement le principe de la fonction std::sort : tu n'as qu'à donner le début et la fin de la collection à trier ainsi qu'une fonction, une expression lambda ou un prédicat permettant de comparer deux données entre elles ;)

                • Partager sur Facebook
                • Partager sur Twitter
                Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs  à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
                  4 octobre 2020 à 13:12:15

                  Merci beaucoup pour ton aide. Problème résolu !
                  • Partager sur Facebook
                  • Partager sur Twitter

                  Template de Class

                  × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                  × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                  • Editeur
                  • Markdown