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 :

Fonctions multi types


Sujet :

C++

  1. #1
    Membre régulier
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Points : 84
    Points
    84
    Par défaut Fonctions multi types
    Bonsoir,

    Je suis confronté à un problème qui relève plus de l'ordre structurel que syntaxique et je côtoie le C++ uniquement dans le cadre d'un projet que je mène pour mes études en IUT. Je ne suis donc pas si familier que ça avec ce langage.

    Bref, j'ai réalisé une application de gestion de la liste des membres d'une association (un CRUD basique).
    J'ai déclaré donc un type complexe Membre mais j'aimerai aussi gérer les produits que cette association met en vente. On a ainsi le type Produit (tout aussi complexe que Membre).
    J'ai écris toutes mes fonctions de lecture/écriture/suppression pour les membres et tout fonctionne.
    Seulement voila, lorsque je me penche sur Produit, je suis obligé de réécrire mes fonctions en changeant uniquement les références aux membres par des références aux produits. C'est légèrement inutile.

    Mon application est axée autour de tableaux représentant à tout instant le contenu de fichiers de sauvegarde lorsque l'application fonctionne.
    Ces fichiers sont chargés au lancement et modifiés dès qu'il y a un changement.
    A ce stade mes fonctions utilisent le tableau de type Membre codé en dur dans leur corps. J'aimerai modifier cela pour le passer en paramètre de chaque fonction pour solutionner le problème évoqué au dessus.
    je me retrouve bloqué par le type du paramètre (que ce soit une variable, un pointeur ou une référence).

    Est-ce que quelqu'un aurait une solution plus fiable à m'apporter?

    Merci par avance, bonne soirée.

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

    Informations professionnelles :
    Activité : aucun

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

    Le fait est qu'un membre et un produit n'ont... strictement rien en commun, même si, effectivement, on peut estimer qu'un membre manipule (achète /vend) des produits

    Lorsque tu gères tes membres et tes produits, tu dois donc, pour ton plus grand malheur, effectivement faire la distinction entre les deux.

    Ainsi, un produit est, à peu de chose près, défini par sa désignation, son identifiant unique éventuel et son prix, alors qu'un membre est défini, toujours à peu de chose près, par son nom, son prénom, sa date de naissance éventuelle, son adresse éventuelle et son identifiant unique éventuel

    Même si on peut considérer que les deux sont susceptibles d'être définis par un identifiant unique, je doute qu'il soit opportun de faire cohabiter les deux dans une collection unique...

    Cela impliquerait qu'un membre pourrait être vendu, ce que, je l'espère du moins honnêtement, ton association refusera avec véhémence

    Par contre, il est vrai que, quand tu ajoute un élément à l'une de tes collection (de membre ou de produit), ou lorsque tu effectue une recherche, la logique peut être sensiblement pareille, mais, et j'insiste vraiment sur ce point, cela n'implique absolument pas qu'il y ait quoi que ce soit en commun entre ces deux types de données: tout au plus, on peut estimer qu'ils présentent une interface identique, par exemple, pour ce qui est de la comparaison

    Si j'insiste là dessus, c'est pour te faire comprendre qu'il ne faut en aucun cas tenter de faire rentrer ces deux types particuliers dans une hiérarchie commune (par exemple, les faire tous les deux hériter d'identifiable ), mais que, par contre, il est possible d'utiliser le paradigme de "programmation générique" pour t'éviter bien du mal et des copies inutiles.

    Seulement, suite à l'épidémie de panne de boule de crystal dont ont souffert les habitués du forum, je suis dans l'impossibilité pour l'instant de confirmer ce que je pressens, et je fais donc peut être tout à fait fausse route

    Pourrais tu donc nous présenter ce que tu as déjà fait, de manière à ce que nous puissions agir sur base d'autre chose que des intuitions qui risquent d'être totalement fausse
    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
    Membre régulier
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Points : 84
    Points
    84
    Par défaut
    Whaou, merci pour cette super réponse

    En fait je n'ai pas l'intention de réunir les deux collections. Les deux tableaux distincts était bien conservés dans mon idée.

    J'aurais pas contre aimé les passer en paramètre des fonctions qui réalisent le CRUD pour éviter de refaire à chaque fois des codes barbant sur la gestion des fichiers.
    Au niveau de ces opérations (pas du reste on est d'accord), les Membre et les Produit ont bien quelque chose en commun de mon point de vue : ce sont tous deux des objets comportant des propriétés scalaires et qui sont enregistrés dans des fichiers.
    Le programme doit tout de même pouvoir exploiter ces similitudes.

    Je joint une partie de mon code à ce message :
    http://root.infos-reseaux.com/files/...mbe/projet.zip
    L'application fonctionne sur la base d'un serveur HTTP (non fourni) qui utilise la fonction appel() suivant la requête qui est faite.
    Je n'ai pas eu le temps de commenter plus que ca pour l'instant (je travaille seul sur mes sources).
    Les fonctions CRUD sont déclarées dans la deuxième partie d'application.cpp et ce sont celle qui commencent par "membres_" ou "produits_".

    Citation Envoyé par koala01 Voir le message
    Salut,
    Cela impliquerait qu'un membre pourrait être vendu, ce que, je l'espère du moins honnêtement, ton association refusera avec véhémence
    Cela dépendra de l'état des comptes de l'association

    Merci pour ton aide.

  4. #4
    Membre chevronné
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Points : 1 921
    Points
    1 921
    Par défaut
    si tu veux utilisze l'héritage pour éviter de réécrire du code, je te conseille de t epencher sur l'héritage privé.

  5. #5
    Membre actif

    Profil pro
    Inscrit en
    Avril 2010
    Messages
    356
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 356
    Points : 206
    Points
    206
    Par défaut
    Si j'ai bien compris, tu aimerais avoir des fonctions qui puissent s'exercer sur des membres et des produits ?
    Si tel est le cas je te conseillerais les templates. Regarde les tutoriels de ce forums pour en savoir plus. Ils sont biens faits

  6. #6
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Si j'ai bien compris ton premier message, que j'ai un peu survolé (ma boule de cristal souffre des mêmes déboires que celle de Koala), en fait, ce que tu veux gérer en commun, ce ne sont pas les produits et les membres, mais le fait que tu as des choses pour gérer un ensemble de (produits/membre/autre...).

    Les seuls points communs entre produit et membre, c'est qu'ils peuvent être créés, détruits, sauvés dans un fichier, lus depuis un fichier, et ce par une interface identique, mais la similitude se limite là, et avoir un conteneur mélangeant produits et membre n'aurait pas de sens, par exemple.

    La solution à ce genre de situations en C++ se nomme template.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  7. #7
    Membre régulier
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Points : 84
    Points
    84
    Par défaut
    Ah merci à tous pour vos réponse, je vais creuser du côté des templates.

    Je viendrai donner une solution si j'en trouve une ici.

  8. #8
    Invité
    Invité(e)
    Par défaut
    Bonjour,
    J'ai eu un peu ce type de problème. Je vais le décrire.
    J'ai des objets de 3 types
    Les points, en grox X,Y,Z
    Les lignes, en gros liste de points
    Les objets, en gros, n'importe quoi.
    Ces 3 type sont aussi différents les une des autres que sont des membres et des produits. Par contre, il ont en point commun leur matricule.
    La gestion des matricule est importante (volume performances etc).
    Voila comment j'ai procédé
    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
     
    class TMatricule
    {
      char radical[8];
      int indice;
      long type; // cette variable contient un certain nombre de flags
      TList *LesDates; // cette classe est utilisée pour l'historique
    public:
      TMatricule(...) // Constructeur :je dois en avoir une dizaine
      ~TMatricule();  // destructeur
      void ImpMat();  // un certain nombre de fonctions 
    };
    class Point3D
    {
      TMatricule *Mat;
      long X,Y;
      float Z;
    public:
      Point3D(...); // constructeur, il y en a aussi pas mal
      ~Point3D(); // le destructeur 
    };
    class TLigne
    {
      TMatricule *Mat;
      ...
    };
    class TObjet
    {
      TMatricule *Mat;
      ...
    };
    //Dans les constructeurs de Point3D, TLigne, TObjet, j'ai un appel de la forme
    Point3D::Point3D(mat, ...)
    {
      Mat=new TMatricule(mat,...);
    ...
    }
    Performance, simplicité et efficacité garantie.

    Cordialement.

  9. #9
    Membre régulier
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    70
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 70
    Points : 88
    Points
    88
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Le fait est qu'un membre et un produit n'ont... strictement rien en commun,
    En fait, un membre et un produit ont les choses suivantes en commun :

    - ils sont capables de se lire/écrire (autrement dit sérialiser et désérialiser) à partir d'un stockage (fichier, base de données, ce n'est pas la question ici)
    - ils implémentent un CRUD donc t'as probablement des méthodes pour valider une modification et l'écrire, voire des transactions ?
    - ils ont aussi probablement des méthodes de recherche pour trouver un objet qui correspond à certains critères (champX = valeur ...)

    Malheureusement pour toi, C++ n'implémente pas (directement) la réflexion, donc tu ne vas pas pouvoir programmer ça de façon générique.

    Tu vas donc, hélas, devoir recopier beaucoup de code, comme tu le dis dans ton premier post.

    En fait, tu es en train de faire un ORM basique, à la main.

    Les capacités de C++ à générer du code via des templates peuvent t'aider, mais de façon très limitée. Généralement, ce problème est traité par un fichier de déclaration qui sert ensuite à générer du code C++, ce qui est peu pratique, mais toujours plus rapide que de le faire à la main.

    Pour ce genre de truc, je te conseille python et SQLAlchemy, qui implémentent ton CRUD hyper facilement. C++ n'est pas dans son domaine fort pour cette application.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par peufeu Voir le message
    En fait, un membre et un produit ont les choses suivantes en commun :

    - ils sont capables de se lire/écrire (autrement dit sérialiser et désérialiser) à partir d'un stockage (fichier, base de données, ce n'est pas la question ici)
    - ils implémentent un CRUD donc t'as probablement des méthodes pour valider une modification et l'écrire, voire des transactions ?
    - ils ont aussi probablement des méthodes de recherche pour trouver un objet qui correspond à certains critères (champX = valeur ...)
    Soit, mais il n'ont rien de commun au sens objet du terme: tu ne peux pas faire passer un membre pour un article, ni inversement, et tu ne peux pas envisager de les maintenir "mélangés" les uns avec les autres.

    Ils ont, il est vrai, une interface commune, mais c'est bien tout
    Malheureusement pour toi, C++ n'implémente pas (directement) la réflexion, donc tu ne vas pas pouvoir programmer ça de façon générique.

    Tu vas donc, hélas, devoir recopier beaucoup de code, comme tu le dis dans ton premier post.
    Non, parce que, justement; étant donné qu'ils ont, malgré tout, une interface commune, tu peux utiliser les template pour profiter de cette interface commune.

    Imaginons les deux classes Membre et Article.

    Elle prendront sans doute 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
    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
    class Article
    {
        public:
            void charger(ifstream &)
            {
                /* ce qu'il faut pour lire les informations concernant l'article */
            }
            void enregistrer(ofstream &)
            {
                /* ce qu'il faut pour enregistrer correctement les informations
                 * concertnant l'article
                 */
            }
            bool operator<(Articles const &) const
            {
                /* ce qu'il faut pour décider si un article est considéré
                 * comme plus petit qu'un autre (pour les trier)
                 */
            }
            bool operator==(Article const &) const
            {
                /* ce qu'il faut pour décider si un article correspond bien
                 * à un autre
                 */
            }
            /* quelques fonctions adaptées aux articles */
        private:
            /* les données relatives à un article particulier:
             * un identifiant éventuel
             * une désignation
             * un ou plusieurs prix
             * une quantité en stock
             * ...
             */
    };
    class Membre
    {
        public:
            void charger(ifstream &)
            {
                /* ce qu'il faut pour lire les informations concernant le membre*/
            }
            void enregistrer(ofstream &)
            {
                /* ce qu'il faut pour enregistrer correctement les informations
                 * concertnant le membre
                 */
            }
            bool operator<(Articles const &) const
            {
                /* ce qu'il faut pour décider si un membre est considéré
                 * comme plus petit qu'un autre (pour les trier)
                 */
            }
            bool operator==(Article const &) const
            {
                /* ce qu'il faut pour décider si un membre correspond bien
                 * à un autre
                 */
            }
            /* quelques fonctions adaptées aux membres */
        private:
            /* les données relatives à un membre particulier:
             * un identifiant éventuel
             * son nom
             * son prénom
             * son adresse éventuelle
             * sa date de naissance éventuelle
             * la date de son entrée dan l'association
             * ...
             */
    };
    Comme les données qui "définissent" un membre ou un article particulier peuvent être tout à fait différente, tu n'aura, effectivement, pas d'autre choix que de... coder pour les deux les fonctions charger, enregistrer et les opérateurs < et ==

    Mais, une fois que tu as fait cela, le paradigme générique vient à ton secours:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    template<class iterator>
    void save(iterator beg, iterator end, std::string const & filename)
    {
        std::ofstream ofs(filename.c_str())
        for(iterator it=beg, it!=end, ++it)
            it->enregistrer(file);
    }
    Cela fonctionnera aussi bien pour un membre que pour un article, parce qu'ils présentent tous les deux... une fonction enregistrer

    Tu peux, de même, utiliser les algorithmes de la stl:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    std::vector<Article> lesArticles;
    /* tu remplis le vecteur avec les différents articles */
    std::vector<Membre> lesMembres;
    /* tu remplis le vecteur avec les différents membres */
    /* puis tu trie tout cela */
    std::sort(lesArticles.begin(), lesArticles.end());
    std::sort(lesMembres.begin(), lesMembres.end());
    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

  11. #11
    Membre régulier
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Points : 84
    Points
    84
    Par défaut
    Citation Envoyé par peufeu Voir le message
    Pour ce genre de truc, je te conseille python et SQLAlchemy, qui implémentent ton CRUD hyper facilement. C++ n'est pas dans son domaine fort pour cette application.
    Je suis totalement d'accord avec toi peufeu, je ferait remonter ca à mes profs qui ont eu cette bonne idée que de retenir le C++.

    Citation Envoyé par koala01 Voir le message
    Ils ont, il est vrai, une interface commune, mais c'est bien tout
    Oui oui, nous évoquions bien, peufeu et moi les similitudes de l'interface, pas du contenu.

    Pour ce que qui est de ce tu proposes koala, j'ai pensé à quelque chose de plus simple (mais qui ne marche pas).
    J'évite de déclarer une classe (je ne suis pas sur que ca plaise au correcteur vu que ca ne rentre pas dans le programme de l'année) et je garde mes structures.

    Dans application.h:
    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
     
    // Déclaration des structures
    struct Ressource {
    	bool present;
    };
    struct Produit : public Ressource{
    	char nom[NOM_MAXLENGTH];
    	char desc[DESC_MAXLENGTH];
    	float prix;
    	float prix_non_membre;
    	int quantite;
    };
     
    struct Membre : public Ressource{
    	char nom[NOM_MAXLENGTH];
    	char prenom [NOM_MAXLENGTH];
    	Date date_adhesion;
    	char mail[NOM_MAXLENGTH];
    };
     
    // templates
    template <typename ResType>
    Dans application.cpp (un extrait seulement concernant la publication d'infos dans un des fichiers).
    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
     
    // Recherche d'une place libre dans le fichier de sauvegarde
    int res_placeLibre (){
    	int n = 0;
    	int p = -1;
     
    	FILE* fichier = fopen(file_url, "rb");
    	if (fichier != NULL){
    		ResType record;
    		while (p < 0 && !feof(fichier)){
    			fread(record, sizeof(record), 1, fichier);
    			if (record.present == false) {
    				p=n;
    			}
    			n++;
    		}
     
    		fclose (fichier);
    	}else{
    		// FILE_ERROR
    	}
     
     
    	return p;
    }
     
    // Publication des infos dans le fichier de sauvegarde
    int res_postInfos (char *buffer, int index, ResType record){
    	bool nouveau = false;
    	if (index >= 0){
    		record.present = true;
    	}else{
    		index = res_placeLibre <ResType>();
    		nouveau = true;
    	}
     
    	bool result = false;
    	FILE* fichier = fopen (file_url, "wb");
    	if (fichier != NULL){
    		fseek(fichier, index * sizeof(record), SEEK_SET);
    		fwrite(record, sizeof(record), 1, fichier);
    		result = true;
    		fclose (fichier);
    	}else{
    		// FILE_ERROR
    	}
     
    	if (result){
    		char id_str[5];
    		sprintf(id_str, "%d", index+1);
    		strcat(buffer, "<post><success>1</success>");
    		if (nouveau){
    			strcat(buffer, "<new_id>");
    			strcat(buffer, id_str);
    			strcat(buffer, "</new_id>");
    		}
    		strcat(buffer, "</post>");
    	}else{
    		Erreur error;
    		strcpy(error.code, "WRITE_ERROR");
    		strcpy(error.titre, "L'ecriture dans le fichier a echoue pour une raison inconnue.");
    		pushErreur(error);
    	}
     
    	return index;
    }
    Et enfin pour appeler tout ce monde:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    Membre record;
    // ... Ajout d'infos dans record
     
    // POST
    res_postInfos <Membre>(buffer, index, record);
    Mais deux problème se posent :
    1. Le compilateur me dit que le type ResType n'est pas déclaré quand il le rencontre dans la signature des fonctions
    2. Même les templates ne me permettent pas de répondre à la question "Quel fichier faut-il ouvrir?".
    Car j'ai changé entre temps de stratégie de stockage : les tableaux MEMBRES et PRODUITS me limitaient dans le nombre d'enregistrements possible.
    Maintenant les fonctions CRUD écrivent en direct dans le fichier sans utiliser de tableau.

    Visiblement j'ai mal compris ce qu'était une template et si vraiment il n'est pas possible de procéder comme je le fais, j'utiliserai tes classes.
    Mais d'ailleurs, n'est-il pas possible de déclarer une classe "Ressource" qui serait étendue par deux autres classes "Membre" et "Produit"?

  12. #12
    Invité
    Invité(e)
    Par défaut
    Juste un petit bonsoir, Fanfouer,

    Si j'ai bien lu, les points communs à "membre" et à "produit" sont les mots "read" et "write" ou leur synonymes, tout le reste est propre à l'objet concerné.
    Naturellement des synonymes de synonymes de synonyme sont satisfaisants pour l'esprit, mais est-ce vraiment constructif et surtout efficace?
    D'accord, il est amusant d'écrire une application qui utilise des tas de sophistications offertes par le C++, mais est-ce là le but?
    Les lectures et écritures sont l'une choses les plus faciles en programmation, puisqu'elle ne nécessitent aucune logique, alors pourquoi se compliquer la vie, autant réserver son temps et son énergie à des choses plus intéressantes.

    Il est possible aussi que je n'ai rien compris à la question, en ce cas, désolé .
    Cordialement.

  13. #13
    Membre régulier
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Points : 84
    Points
    84
    Par défaut
    Citation Envoyé par Pierre Dolez Voir le message
    Juste un petit bonsoir, Fanfouer,
    Bien le bonsoir Pierre Dolez

    Les lectures et écritures sont l'une choses les plus faciles en programmation, puisqu'elle ne nécessitent aucune logique, alors pourquoi se compliquer la vie, autant réserver son temps et son énergie à des choses plus intéressantes.
    Je suis bien d'accord avec cette contribution, ce projet ne me passionne pas plus que ça et j'ai tas d'autres choses à faire à côté.
    Cependant j'ai passé mon après-midi à tenter diverses combinaisons possibles, avec classes ou sans classes et sans rien du tout de pomme de terre. Quitte à faire la modification, autant la faire jusqu'au bout.
    Certes ca changera pas grand chose que j'écrive une fonction générale ou une fonction spécifique à chaque type. J'ai par contre envie (et c'est un choix personnel) de rendre quelque chose d'abouti à mon correcteur. Chose que quelqu'un d'autre n'aurait peut-être pas fait.


    Le problème qui se pose à présent ne me semble pas compliqué pour un connaisseur des templates. Il suffit juste de m'expliquer l'erreur que j'ai fait.
    Après si vraiment c'est trop complexe à résoudre, et bien j'écrirai 8 fonctions (rien que pour être dans les temps, ma soutenance est la semaine prochaine).

    Merci et bonne nuit

  14. #14
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par fanfouer Voir le message
    J'évite de déclarer une classe (je ne suis pas sur que ca plaise au correcteur vu que ca ne rentre pas dans le programme de l'année) et je garde mes structures.
    Classes et structures, c'est presque exactement identique. Par contre, si tu n'as pas vraiment vu ce que c'est, je doute que tu aies vu comment te servir des templates. J'ai l'impression qu'il va t'être difficile de faire du code propre (objectif très louable) avec tes connaissances actuelles.
    Citation Envoyé par fanfouer Voir le message

    Dans application.h:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    // Déclaration des structures
    struct Ressource {
    	bool present;
    };
    struct Produit : public Ressource
    Pourquoi utiliser de l'héritage ici ? Surtout si tu n'es pas sensé avoir vu ce qu'est une classe, je doute que la programmation orientée objet t'ai été présentée. Et en plus, en l'occurrence, elle ne s'applique pas trop (relire les premiers messages). J'ai à peine apercu le reste de ton code, mais il ressemble plus à du C qu'à du C++. En particulier, utiliser sd::string pourrait être intéressant.
    Citation Envoyé par fanfouer Voir le message
    Mais deux problème se posent :
    1. Le compilateur me dit que le type ResType n'est pas déclaré quand il le rencontre dans la signature des fonctions
    2. Même les templates ne me permettent pas de répondre à la question "Quel fichier faut-il ouvrir?".
    Car j'ai changé entre temps de stratégie de stockage : les tableaux MEMBRES et PRODUITS me limitaient dans le nombre d'enregistrements possible.
    Maintenant les fonctions CRUD écrivent en direct dans le fichier sans utiliser de tableau.
    Te limitaient dans quel sens ? Parce qu'ils étaient de taille fixe (dans ce cas, je te conseilles de regarder vector) ? Ou parce que tu as trop de données pour toutes les avoir en mémoire en même temps (et même dans ce cas, je conseillerais d'éviter d'écrire tout directement sur disque, sans passer par une zone mémoire intermédiaire) ?
    Citation Envoyé par fanfouer Voir le message
    Visiblement j'ai mal compris ce qu'était une template et si vraiment il n'est pas possible de procéder comme je le fais, j'utiliserai tes classes.
    Mais d'ailleurs, n'est-il pas possible de déclarer une classe "Ressource" qui serait étendue par deux autres classes "Membre" et "Produit"?
    Pour ta question 2, les templates te permettent de le faire... La question de base est qu'est-ce qui est commun à tes différentes ressources (gestion de liste) et qu'est-ce qui est différent (nom de fichier). Ce qui est commun, tu le définis à l'aide de template, ce qui est différent à l'aide de fonctions surchargées, de fonctions différentes dans des classes différentes... mais en utilisant des noms identiques.

    Exemple (non compilé, et écrit à la va-vite, juste pour donner les concepts) :

    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
    struct Produit
    {
      static string nomDeFichier() {return "Produit.db";} // Spécifique
      void save (ostream &os) {/*...*/} // Spécifique
      string nom;
      double prix;
    };
     
    struct Membre
    {
      static string nomDeFichier() {return "Membre.db";} // Spécifique
      void save (ostream &os) {/*...*/} // Spécifique
      string nom;
      string mail;
    };
     
    template <class T>
    struct GestionnaireDeRessources // Commun
    {
      vector<T> mesDonnees;
      void save()
      {
        ofstream ofs(T::nomDeFichier()); // Le commun fait appel au spécifique
        for (size_t i = 0 ; i<mesDonnees.size() ; ++i)
        {
            mesDonnees.save(ofs); // Le commun fait appel au spécifique
        }
      }
    }
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  15. #15
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par Pierre Dolez Voir le message
    Si j'ai bien lu, les points communs à "membre" et à "produit" sont les mots "read" et "write" ou leur synonymes, tout le reste est propre à l'objet concerné.
    Naturellement des synonymes de synonymes de synonyme sont satisfaisants pour l'esprit, mais est-ce vraiment constructif et surtout efficace?
    Je pense que oui ! C'est à la base de la programmation générique. Une fois que des choses semblables peuvent s'écrire de manière uniforme, on peut écrire un code unique qui marche avec des tas de choses.

    Exemple, lié à la sauvegarde de données : Certains se sont rendus compte que, à haut niveau, sauver ou lire des données, c'était la même chose. Dans un langage avec de la réflexion, ça consiste à découvrir la structure d'une classe, soit pour en lire un à un les membres, soit à écrire un à un les membres. Dans un langage sans réflexion, comme le C++, ça consiste à ce que le développeur d'une classe décrive sa structure. Ce qui fait que pour sérialiser une classe, au lieu de devoir écrire une fonction pour lire, et une pour écrire, une seule suffit (par exemple avec boost::serialization) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    template<class Ar> MaClasse::serialize(Ar &archive)
    {
      ar & maDonneeMembre1 & maDonneeMembre2;
    }
    Deux fois moins de code à écrire, deux fois moins de code à maintenir.
    Citation Envoyé par Pierre Dolez Voir le message
    D'accord, il est amusant d'écrire une application qui utilise des tas de sophistications offertes par le C++, mais est-ce là le but?
    C'est rarement un but, sauf en phase d'apprentissage. Le but de ces outils spohistiqués est de permettre d'écrire un code plus simple, plus rapidement
    Citation Envoyé par Pierre Dolez Voir le message
    Les lectures et écritures sont l'une choses les plus faciles en programmation, puisqu'elle ne nécessitent aucune logique, alors pourquoi se compliquer la vie, autant réserver son temps et son énergie à des choses plus intéressantes.
    Je ne suis pas d'accord. Tout d'abord il y a de la logique à écrire derrière, dès qu'on s'éloigne des cas simples. Ensuite, ça peut être très long et fastidieux (et donc source de bugs) à écrire dans un vrai programme. Ça mérite donc qu'on s'arrête un peu pour réfléchir comment simplifier tout ça.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  16. #16
    Membre régulier
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Points : 84
    Points
    84
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Par contre, si tu n'as pas vraiment vu ce que c'est, je doute que tu aies vu comment te servir des templates. J'ai l'impression qu'il va t'être difficile de faire du code propre (objectif très louable) avec tes connaissances actuelles.Pourquoi utiliser de l'héritage ici ? Surtout si tu n'es pas sensé avoir vu ce qu'est une classe, je doute que la programmation orientée objet t'ai été présentée.
    En fait, je pensais savoir ce qu'était de l'OO. Je pratique beaucoup la programmation mais avec des langages web (genre PHP) ou ActionScript qui implémentent certains concepts mais pas tous et parfois différemment que ce qui semble se faire en C++ (je ne parle même pas de JavaScript )
    Généralement, quand je pense à hiérarchiser/répartir/organiser mes chaines de traitement, je pense à des classes. Le C introduit les structures et les types (qui me bloquent systématiquement au dernier moment ) qui sont des notions diffuses pour moi.

    J'ai à peine apercu le reste de ton code, mais il ressemble plus à du C qu'à du C++.
    Il faut voir ça avec mon prof, je n'ai pas vu dans mon cours où s'établissait la limite entre C et C++ (il semblerait tout de même que ce soit du C++ puisque j'utilise des booléens dont le type primitif n'existe pas en C).

    L'héritage intervient entre Ressource et Membre/Produit puisque l'attribut présent s'applique à toutes les ressources.
    Ca permet de savoir si l'enregistrement a été supprimé du fichier : c'est la seule méthode d'effacement qu'on ai apprise. Mettre le flag présent à false revient à supprimer l'enregistrement.

    En particulier, utiliser sd::string pourrait être intéressant.
    Je n'en ai jamais entendu parler.

    Te limitaient dans quel sens ? Parce qu'ils étaient de taille fixe (dans ce cas, je te conseilles de regarder vector) ?
    Oui, c'est exactement ça.
    Vector fais parti de STL si j'ai bien compris?
    Mais de toutes façons, écrire dans le fichier à chaque modification ne me déplais pas. Ça évite en gros de gérer la synchro entre une liste en RAM et une liste en ROM.

    Pour ta question 2, les templates te permettent de le faire... La question de base est qu'est-ce qui est commun à tes différentes ressources (gestion de liste) et qu'est-ce qui est différent (nom de fichier). Ce qui est commun, tu le définis à l'aide de template, ce qui est différent à l'aide de fonctions surchargées, de fonctions différentes dans des classes différentes... mais en utilisant des noms identiques.
    Mais est-on obligé de faire appel à des classes?
    Ne peut-on pas, en toute simplicité (simplicité relative n'est-ce pas ), déclarer des fonctions dans le .cpp qui effectuent des traitements sur des variables structurées (les "Type record" dans mes fonctions) à l'aide de template?

    Si je pose cette question, c'est que dans tous les exemples du cours que j'ai pratiqué, tout ce qu'on a enregistré dans des fichiers ce sont des variables structurées (ou carrément des tableaux content des variables structurées).
    Je n'ai pas d'autre expérience.
    C'est pour ca que, par exemple, je ne saurais pas trop quoi mettre dans la méthode "save" de chacune des structures que tu proposes. Est-ce l'objet lui-même qui est sérialisé puis écris? Est-ce autre chose... aucune idée.

    Petite précision : je suis en Réseaux & Télécoms et non en IUT Informatique. C'est pas notre cœur de métier et ca peut expliquer pourquoi le cours est approximatif sur certains points.

    Merci à tous pour vos conseils en tout cas.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par fanfouer Voir le message
    Il faut voir ça avec mon prof, je n'ai pas vu dans mon cours où s'établissait la limite entre C et C++ (il semblerait tout de même que ce soit du C++ puisque j'utilise des booléens dont le type primitif n'existe pas en C).
    C'est le problème de nombreux profs qui apprennent plutôt le "C with class" que le C++, et qui croient (réellement) faire du C++

    De manière générale:
    Si tu inclus un fichier d'en-tête standard dont l'extension est .h, c'est du C
    Si tu utilise une fonction spécifique que tu retrouve en C (*scanf, *printf strcat, strcmp,atof, atoi,...), c'est du C
    Si tu utilise malloc, calloc, realloc et free, c'est du C
    Si tu programme de manière générique (template) ou en Orienté Objet, c'est du C++
    L'héritage intervient entre Ressource et Membre/Produit puisque l'attribut présent s'applique à toutes les ressources.
    Ce qu'il faut comprendre, c'est:
    1- L'orienté objet s'intéresse plus aux comportements, et non aux données
    2- L'héritage n'est justifié que lorsque tu as une relation EST-UN, c'est à dire, si n'importe quel objet d'un type dérivé peut être utilisé à la place d'un objet du type de base

    Il faut cependant veiller à la granularité dans le raisonnement qui te fait estimer que tu as bien une relation EST-UN...

    En effet, dans l'absolu, une voiture, une louche et une marguerite ont toutes une relation avec un type Objet. On pourrait donc estimer (c'est ce que fait java, par exemple) que l'on peut faire dériver voiture, louche et marguerite de... Object.

    Seulement, Object est tellement "imprécis" qu'il... ne sert strictement à rien de vouloir faire cohabiter ces trois objets dans un tableau commun: Bien qu'il y ait sans doute quelques points communs, ils sont très largement insuffisants pour te permettre de manipuler une voiture, une louche et une marguerite de manière utile en les faisant passer pour des Objects.

    Ce que j'essaie de te faire comprendre, c'est que si tu n'a qu'un seul attribut commun, ce n'est clairement pas la peine d'envisager un héritage, car, en toute logique, tu n'utilisera pas ces deux objets de manière identique.

    J'ai peut être eu tord de mettre un peu trop d'humour en parlant de vendre un des membres de l'association, mais c'est exactement le risque que l'héritage te ferait prendre.

    La preuve en est que, de manière générale, tu sauvegardera les membres avec les membres et ... les produits avec les produits, tu ne va pas commencer à mélanger (dans un fichier ou dans une base de données) les membres et les produits.

    Si tu ne les mélanges pas dans un fichier ou dans une base de données, quelle raison aurais tu de les mélanger... dans ton application
    Ca permet de savoir si l'enregistrement a été supprimé du fichier : c'est la seule méthode d'effacement qu'on ai apprise. Mettre le flag présent à false revient à supprimer l'enregistrement.
    Et cela ne sert à rien

    C'est encore plus vrai si tu travailles sur un fichier xml, car, pour être un minimum "bien formé" il doit prendre une forme proche de:
    Code XML : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <data>
        <membres nombre="12">
            <membre id="1"> <!-- tout le membre --!></membre>
            <!-- les 11 autres membres --!>
        </membres>
        <produits nombre="54">
            <produit id="1"> <!-- tout le produit --!> </produit>
            <!-- les 53 autres produits --!>
        </produits>
    </data>
    Cela implique que, de toutes manières, toute modification apportée au fichier (qu'il s'agisse d'ajouter ou de retirer un produit ou un membre) provoquera... la réécriture complète du fichier, étant donné qu'il est impossible d'insérer ou de retirer des données "au beau milieu" du fichier.

    Si tu veux éviter de réécrire un fichier qui n'a pas été modifié, c'est au niveau de ta collection de membre(s) et / ou de ta collection de produits que cela se gère: si on en a ajouté, modifié ou retiré un, il faut réécrire le fichier, si on s'est contenté d' "interroger" les différents éléments, ce n'est pas la peine de le faire
    Je n'en ai jamais entendu parler.
    Voilà qui est bien malheureux...

    Ta structure produit pourrait, par exemple, prendre la forme de

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    struct Produit { //j'espère t'avoir convaincu que l'héritage ne sert à rien ;)
    	std::string nom;
    	std::string desc;
    	float prix;
    	float prix_non_membre;
    	int quantite;
    };
    qui pourrait être utilisée 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
    void foo()
    {
        if(monproduit.nom=="Lait demi ecreme")
        {
            /* que faire si c'est le cas */
        }
        else
        {
            /* que faire autrement */
        }
    }
    Oui, c'est exactement ça.
    Vector fais parti de STL si j'ai bien compris?
    Oui, tout comme la classe string... Comme d'ailleurs, tous les types "complexes" fournis par le langage.

    STL signifie "Standard Templated Library" et fourni un nombre très important de classes, de structures et d'algorithmes particulièrement sympa
    Mais de toutes façons, écrire dans le fichier à chaque modification ne me déplais pas. Ça évite en gros de gérer la synchro entre une liste en RAM et une liste en ROM.
    De toutes façons, il ne faut même pas réécrire le fichier "à chaque modification"... il faut le réécrire:
    1- à la demande de l'utilisateur
    2- au plus tard avant de quitter ton application, si cela se justifie
    3- éventuellement, après un délais donné (toutes les XXX minutes ou les YYY secondes)


    Mais est-on obligé de faire appel à des classes?
    Ne peut-on pas, en toute simplicité (simplicité relative n'est-ce pas ), déclarer des fonctions dans le .cpp qui effectuent des traitements sur des variables structurées (les "Type record" dans mes fonctions) à l'aide de template?
    En C++, la seule différence qu'il y a entre une structure et une classe tient dans la visibilité par défaut des membres et de l'héritage: publique pour les structure et privée pour les classes.

    Cette différence excepté, tu peux utiliser les classes et les structures de manière totalement identique

    La bonne question serait beaucoup plus, dans ton cas, "est-il préférable d'avoir une approche orientée objet ou une approche purement procédurale "

    Et il faut avouer que l'approche orientée objet présente des avantages des plus intéressants:
    1- Elle favorise la subsituabilité (le fait de faire passer un objet d'un type "spécialisé" pour un objet d'un type plus "générique" grâce à l'héritage), même si, ici, tu n'en a pas vraiment besoin

    2- Elle facilite la mise en place de l'encapsulation (le fait d'empêcher les fonctions d'accéder à des membres de manière indue). Elle est possible en procédurale (personne ne doit savoir de quoi est composée FILE pour l'utiliser en C ), mais c'est toute une histoire pour la mettre en oeuvre

    3- Elle incite à réfléchir en terme de comportements attendus par les différents objets plutôt qu'en terme de données manipulées.

    Cela permet une plus grande stabilité de la manière dont on utilise l'objet.
    Si je pose cette question, c'est que dans tous les exemples du cours que j'ai pratiqué, tout ce qu'on a enregistré dans des fichiers ce sont des variables structurées (ou carrément des tableaux content des variables structurées).
    La seule différence entre les deux, c'est que tu va utiliser une fonction membre ou une fonction amie pour écrire le contenu de ta classe dans un fichier, plutôt qu'une fonction libre...

    Autrement, c'est du pareil au même: même si un objet est envisagé sur l'angle des comportements qu'il met en oeuvre, il n'empêche qu'il est organisé... exactement de la même manière qu'une variable structurée

    C'est pour ca que, par exemple, je ne saurais pas trop quoi mettre dans la méthode "save" de chacune des structures que tu proposes. Est-ce l'objet lui-même qui est sérialisé puis écris? Est-ce autre chose... aucune idée.
    Tu fais exactement de la même manière que pour n'importe quelle variable structurée...

    La seule différence tient dans le fait que tout part du pointeur THIS qui représente l'objet en cours de manière implicite

    Si, en procédural, tu venais à écrire une fonction save sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void save(ofstram & ofs, Membre const & m)
    {
        ofs<<"<membre id=\"" <<m.id<<"\">"<<std::endl
           <<"\t<nom>"<<m.nom<<"</nom>"<<std::endl
           <<"\t<prenom>"<<m.prenom<<"</prenom>"<<std::endl;
    }
    en orienté objet, cela deviendrait
    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
    /* le passage du membre à enregistrer est implicite
     * on peut, en cas de besoin, récupérer un pointeur dessus
     * grâce à this
     */
    void Membre::save(ofstram & ofs) const
    {
        /* tous les membres sont accessibles directement */
        ofs<<"<membre id=\"" <<id<<"\">"<<std::endl
           <<"\t<nom>"<<nom<<"</nom>"<<std::endl
           <<"\t<prenom>"<<prenom<<"</prenom>"<<std::endl;
        /* il n'y a que si on avait passé un argument nommé id, par
         * exemple, qu'il aurait fallu écrire this->id pour faire la 
         * distinction entre l'argument et le membre du même nom
         */
    }
    (J'ai mis des commentaires... Lis les, car, une fois n'est pas coutume, il sont presque plus importants que le code lui-même )
    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

  18. #18
    Membre régulier
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    70
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 70
    Points : 88
    Points
    88
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Soit, mais il n'ont rien de commun au sens objet du terme: tu ne peux pas faire passer un membre pour un article, ni inversement, et tu ne peux pas envisager de les maintenir "mélangés" les uns avec les autres.
    Oui, bien sûr !

    Citation Envoyé par koala01 Voir le message
    Ils ont, il est vrai, une interface commune, mais c'est bien tout
    Ce qui est en commun entre ces 2 classes est une interface, et un ou plusieurs algorithmes. C'est déjà beaucoup (et source d'une bonne quantité de code à factoriser si on peut).

    Citation Envoyé par koala01 Voir le message
    Comme les données qui "définissent" un membre ou un article particulier peuvent être tout à fait différente, tu n'aura, effectivement, pas d'autre choix que de... coder pour les deux les fonctions charger, enregistrer et les opérateurs < et ==
    C'est ça qui est pénible, car sans réflexion, on ne peut pas le faire de façon générique :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    pour chaque membre de l'objet dans une liste:
       membre -> ecrire dans ( fichier )
    Et on est donc obligé de le faire à la pougne, avec les problèmes de maintenance, documentation etc :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
       membre1 -> ecrire dans ( fichier )
       membre2 -> ecrire dans ( fichier )
       membre3 -> ecrire dans ( fichier )
    Citation Envoyé par koala01 Voir le message
    C'est le problème de nombreux profs qui apprennent plutôt le "C with class" que le C++, et qui croient (réellement) faire du C++
    Oh que oui.

    Citation Envoyé par koala01 Voir le message
    2- L'héritage n'est justifié que lorsque tu as une relation EST-UN, c'est à dire, si n'importe quel objet d'un type dérivé peut être utilisé à la place d'un objet du type de base
    Quoique l'héritage multiple puisse être utilisé pour implémenter des mixins aussi, ce qui n'est pas le moindre de ses intérêts (les mauvaises langues diront que c'est le seul...)

    Citation Envoyé par koala01 Voir le message
    Cela implique que, de toutes manières, toute modification apportée au fichier (qu'il s'agisse d'ajouter ou de retirer un produit ou un membre) provoquera... la réécriture complète du fichier, étant donné qu'il est impossible d'insérer ou de retirer des données "au beau milieu" du fichier.
    Soit dit en passant, SQLite est une excellente alternative aux formats de fichiers codés à l'arrache...

    Citation Envoyé par koala01 Voir le message
    ofs<<"<membre id=\"" <<id<<"\">"<<std::endl
    <<"\t<nom>"<<nom<<"</nom>"<<std::endl
    <<"\t<prenom>"<<prenom<<"</prenom>"<<std::endl;
    Il manque l'échappement des caractères style < dans les chaînes (ce qui est une des raisons pour lesquelles les formats de fichiers codés à l'arrache sont une belle et bonne source de bugs)...

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par peufeu Voir le message
    Ce qui est en commun entre ces 2 classes est une interface, et un ou plusieurs algorithmes. C'est déjà beaucoup (et source d'une bonne quantité de code à factoriser si on peut).
    Le problème, c'est que les comportements attendus au travers de cette interface devront, de toutes manières, être spécialisés pour le type de données que tu utilise...

    Même avec la réflexion apportée par java, il te faudrait définir toi même comment sérialiser une classe proche de
    Code java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    public class MaClass
    {
        private string membre_1;
        private integer membre_2;
        private float membre_3;
    };
    Et ce, même si tu as, juste à coté, une classe qui utilise à peu près les mêmes membres


    C'est ça qui est pénible, car sans réflexion, on ne peut pas le faire de façon générique :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    pour chaque membre de l'objet dans une liste:
       membre -> ecrire dans ( fichier )
    Et on est donc obligé de le faire à la pougne, avec les problèmes de maintenance, documentation etc :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
       membre1 -> ecrire dans ( fichier )
       membre2 -> ecrire dans ( fichier )
       membre3 -> ecrire dans ( fichier )
    A vrai dire, il existe des alternatives, comme boost.serialize, par exemple

    Quoique l'héritage multiple puisse être utilisé pour implémenter des mixins aussi, ce qui n'est pas le moindre de ses intérêts (les mauvaises langues diront que c'est le seul...)
    En toute honnêteté, en quoi l'héritage multiple t'aiderait il ici
    Soit dit en passant, SQLite est une excellente alternative aux formats de fichiers codés à l'arrache...
    Comme tout système de base de données digne de ce nom... A voir seulement si cela apporte réellement quelque chose, ou si cela revient, peu ou prou, à utiliser un canon pour tuer une mouche
    Il manque l'échappement des caractères style < dans les chaînes (ce qui est une des raisons pour lesquelles les formats de fichiers codés à l'arrache sont une belle et bonne source de bugs)...
    Justement, non... pas si tu utilise la std::string

    Les seuls caractères devant être échappés sont ceux qui ont une double signification au sein de la chaine de caractère:
    • t ou \t
    • n ou \n
    • 0 ou \0
    • tous les autres du même cru et, fatalement
    • " ou \" car il représente la fin ... de la chaine de caractères s'il n'est pas échappé

    le code
    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <fstream>
     
    int main()
    {
        std::ofstream ofs("test.txt");
        ofs<<"<Objects>"<<std::endl
           <<"\t<item id=\"1\"> item1</item>"<<std::endl
           <<"</objects>"  <<std::endl;
    	return 0;
    }
    donne bien un fichier contenant
    Code xml : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    <Objects>
    	<item id="1"> item1</item>
    </objects>
    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
    Membre régulier
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    70
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 70
    Points : 88
    Points
    88
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Le problème, c'est que les comportements attendus au travers de cette interface devront, de toutes manières, être spécialisés pour le type de données que tu utilise...
    Je préfère te mettre un exemple (en pièce jointe) de métaprog/réflexion particulièrement couillue (sqlite + SQLAlchemy + Elixir) ... et ici totalement hors sujet puisqu'on parle de C++. Note qu'on doit quand même spécifier la liste des champs puisqu'on a une table SQLite... mais pour une sérialisation transparente y'a pickle ou serialize en php...

    boost.serialize a l'air beaucoup plus digeste que la solution "à la pougne" ceci dit.

    Citation Envoyé par koala01 Voir le message
    En toute honnêteté, en quoi l'héritage multiple t'aiderait il ici
    Par exemple à créer une interface ISerializable avec les méthodes idoines de façon à définir Fichier << objet qui appelle objet->write( Fichier ) pour bénéficier de l'appel de la virtual sur la fonction de sérialisation write()...

    Citation Envoyé par koala01 Voir le message
    Comme tout système de base de données digne de ce nom... A voir seulement si cela apporte réellement quelque chose, ou si cela revient, peu ou prou, à utiliser un canon pour tuer une mouche
    Certes (quoi que SQLite soit plutôt un 22, le canon lourd c'est Postgres)

    Perso je suis devenu de plus en plus allergique aux formats de fichier codés à l'arrache, l'écriture du parser prend en général beaucoup trop de temps XD

    Citation Envoyé par koala01 Voir le message
    Justement, non... pas si tu utilise la std::string
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    int main()
    {
            std::string machaine( "1 est < à 2" );
            std::ofstream ofs("test.txt");
            ofs     << "<Objects>"
                    << std::endl
                    << "\t<item id=\"1\">"
                    << machaine
                    << "</item>"
                    << std::endl
                    << "</objects>"
                    << std::endl;
            return 0;
    }
    Là le fichier XML n'est pas valide puisqu'il contient un < qui traîne... il faut échapper ...
    Fichiers attachés Fichiers attachés
    • Type de fichier : py DB.py (4,1 Ko, 54 affichages)

Discussions similaires

  1. Fonction du type y=
    Par vladskeeper dans le forum Bases de données
    Réponses: 2
    Dernier message: 29/12/2005, 18h54
  2. Réponses: 9
    Dernier message: 22/07/2005, 16h10
  3. Fonction de type between
    Par midnight77 dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 01/04/2005, 14h51
  4. [LG]Resultat de fonction de type Record ?
    Par Mike888 dans le forum Langage
    Réponses: 10
    Dernier message: 24/01/2004, 22h48
  5. Fonction de type tableau
    Par Charles f dans le forum Langage
    Réponses: 5
    Dernier message: 04/08/2002, 14h04

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