IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

[Langage] Problème de tableau de structure


Sujet :

Langage C++

  1. #1
    Membre confirmé

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2007
    Messages
    177
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2007
    Messages : 177
    Par défaut [Langage] Problème de tableau de structure
    Bonjour! Voilà j'ai un petit problème sur la définition d'un tableau de structure.

    entreprise.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
     
    typedef struct Salarie
    {
          int ID;
          const char FirstName[30];
          const char Name[30];
          int Age;
          const char Sexe[1];
    } Salarie;
     
    class entreprise;
     
    class entreprise
    {
    protected:
          entreprise(void);
          ~entreprise(void);
    public:
          static void EnregistreSalarie(Salarie salarie[]);
    ça c'est ma structure. Maintenant dans le main,

    Program.cpp
    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
     
    #include <iostream>
     
    #include "entreprise.h"
     
    /* Fait un test pour l'affichage des salariés rentré dans le tableau */
    void Affiche_Liste_Salarie(Salarie salarie)
    {
          std::cout << "%i %d %d %i %d" << salarie.ID << salarie.FirstName << salarie.Name << salarie.Age << salarie.Sexe << endl;
    }
     
    int main()
    {
          Salarie salarie[1] =
          {
                { 0, "Prenom", "Nom", 25, 'M' },
          };
     
          // Fonction que je doit encore remplir le code
          entreprise::EnregistreSalarie(salarie); // Erreur de VS2008
     
          for(i = 0; i < sizeof(salarie); i++) // Erreur de violation d'adresse mémoire
          {
                Affiche_Liste_Salarie(salarie);
          }
          return 0;
    }
    Erreur :

    Erreur 2 error C2665: aucune des 7 surcharges n'a pu convertir tous les types d'arguments

    J'espère que ça pourra vous aidez à régler mon problème. Merci d'avance pour vos réponses.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 641
    Par défaut
    Salut,

    Il y a déjà, me semble-t-il, un problème de réflexion et de conception à la base.

    En effet, le fait de déclarer une méthode statique rend cette méthode "indépendante de toute instance" de la classe dans laquelle elle se trouve.

    Or, si l'on s'en tient à la seule sémantique de la fonction EnregistreSalarie, deux possibilités sont offertes:

    • Soit tu considère le terme comme le fait de sauvegarder la liste des salariés dans un fichier
    • Soit tu considère le terme comme le fait de faire savoir à une entreprise que tel salariétravaille pour elle.


    Quoi qu'il en soit, pour y arriver, il faut bel et bien que la méthode s'applique... à une entreprise donnée.

    En outre, il devrait y avoir une relation entre l'entreprise et les salariés, dans le sens où l'entreprise devrait... gérer elle-même les salariés qui travaillent pour elle.

    Il semblerait cohérent de déclarer une collection de salariés directement comme membre de entreprise

    Cela aurait en outre l'avantage que tu ne devrais plus te charger de transmettre la collection de salariés... à la méthode EnregistreSalariés

    Cet avertissement étant donné, quelques conseils s'imposent:

    Le premier concerne l'utilisation du mot clé struct.

    Contrairement au C, la déclaration d'un type utilisateur (car c'est valable pour les structures, pour les classes, pour les enumération et pour les unions) provoque automatiquement la définition du type correspondant.

    Il est donc clairement inutile de commencer avec des typedef struct terme{} autre_terme;

    Ensuite, vient le tour du choix des types utilisés pour représenter les différentes informations de tes salariés.

    L'identifiant et - surtout - l'age du salarié n'ont strictement aucune chance d'être négatif.

    Dés lors, pourquoi ne pas déclarer ces valeurs comme étant non signées, et donc strictement supérieures ou égales à 0

    Pour le sexe, il n'y a aucun intéret à le déclarer comme un tableau de caractères ne comprenant que... un seul caractère

    Comme un caractère unique suffit pour représenter le type en question, il est inutile de déclarer ce membre comme un tableau pouvant en contenir plusieurs, mais cela signifie qu'il est donc simplement... inutile de déclarer le sexe comme étant un tableau de caractères

    Autrement dit, un simple char sexe; sera déjà bien amplement suffisant

    Enfin, il faut savoir que la gestion des chaines de caractères à la manière "C style" (sous la forme d'un tableau de caractères terminé par un '\0') est souvent très problématique.

    Même lorsque tu décide d'un nombre immuable de caractères pour représenter le nom ou le prénom, ce qui ne va pas sans poser certains problèmes tels que:
    • Que se passerait il si tu te trouvais face à une personne dont le nom ou le prénom contient... 1 caractère de plus que le nombre que tu as prévu ou que
    • utiliser 2*30 caractères pour maintenir Marc La, ca provoque pas mal de pertes

    la gestion de chaines de caractères "C style" reste particulièrement ardue dés le moment où il s'agit ne serait-ce que de les comparer.

    Le conseil est donc de toujours préférer les possibilités fournies par le standard en termes de classes ou de fonction à toute possibilité à peu près équivalente issue du C.

    Ainsi, nous ne le répèterons jamais assez, mais il est très avantageux de préférer la classe string, disponible dans l'espace de noms std par inclusion du fichier d'en-tête <string> à l'utilisation d'une chaine de caractères "C style".

    De la même manière, le standard fournit une série intéressante de conteneurs particuliers à préférer à l'utilisation de tableau "C style", surtout si les tableaux en question devraient être gérés de manière dynamique.

    Tous ces conteneurs se trouvent également dans l'espace de noms std, et voici ceux qui présenteraient sans doute un intérêt dans ton cas (tu peux te baser sur cette entrée de la FAQ pour déterminer celui qui t'intéresse le mieux )
    1. la classe vector, disponible par inclusion du fichier d'en-tête <vector>, qui agit exactement comme un tableau, particulièrement adapté pour les accès aléatoires aux différents éléments (passer du 1er élément au 10ieme, puis aller au 100eme avant de retourner au 3eme).
    2. La classe list, disponible par inclusion du fichier d'en-tête <list> qui est très efficace sur l'ajout ou la suppression d'éléments et qui y permet un accès "séquentiel"
    3. La classe set , disponible par inclusion du fichier d'en-tête <set>, qui est une structure triée sur laquelle l'élément fait office de clé de tri particulièrement efficace pour la recherche d'éléments, mais qui, en retour, empêche la modification d'un élément existant afin d'empêcher de "foutre le b" dans le tri.
    4. la classe map, disponible par inclusion du fichier d'en-tête <map>; qui est une structure associative permettant de représenter une valeur par une clé et qui effectue le tri sur base de la clé. Elle est également particulièrement adaptée à la recherche, mais elle a l'avantage sur le set d'admettre la modification éventuelle de la valeur

    Toutes ces classes ont en outre un avantage réel sur le tableau C style: elles connaissent leur taille (le nombre d'éléments qu'elles contiennent), car les boucles basées sur for(i = 0; i < sizeof(salarie); i++) (ou salarie est un Salarie* ) ca la fait pas vraiment

    Enfin, il faut savoir que cout est déjà un flux "formaté", il n'est donc absolument pas nécessaire de lui présenter une "chaine de formatage" comme c'est le cas avec le printf du C.

    Cela signifie que la fonction
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void Affiche_Liste_Salarie(Salarie salarie)
    {
          std::cout << "%i %d %d %i %d" << salarie.ID << salarie.FirstName << salarie.Name << salarie.Age << salarie.Sexe << endl;
    }
    sera avantageusement remplacée par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void Affiche_Liste_Salarie(const Salarie& salarie)
    {
          std::cout <<salarie.ID<<" " << salarie.FirstName<<" " 
                    << salarie.Name<<" " << salarie.Age<<" " << salarie.Sexe 
                    << std::endl;
    }
    Tu auras remarqué que j'ai un peu modifié le prototype de la fonction en:
    1. déclarant l'argument nomme salarié comme une référence, afin d'éviter la copie inutile de la variable qui sera passée en paramètre lors de l'appel
    2. déclarant cette référence constante, pour indiquer au compilateur et à l'utilisateur que l'objet ne sera pas modifié en cours d'utilisation


    Au final, voici ce qui pourrait se faire.

    Tu aurais une structure de type Salarié qui prendrait la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    struct Salarie
    {
        unsigned int ID;
        std::string nom;
        std::string prenom;
        char sexe;
        unsigned int age;
    };
    et tu aurais une classe "Entreprise" qui aura la responsabilité de gérer les salariés qu'elle emploie.

    Cette entreprise devra donc être en mesure d'ajouter un salarié chaque fois qu'elle en engagera un, d'afficher l'ensemble des salariés qu'elle emploie, et d'enregistrer l'ensemble de ses salariés

    Nous n'allons pas, pour l'instant, lui demander de les trier, et nous allons simplement utiliser un "accès séquentiel" à ceux-ci

    Elle se présentera donc sous 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
    class Entreprise
    {
        public:
            Entreprise(const std::string n=""); /* l'entreprise a un nom
                                                * facultatif que nous lui donnons 
                                                * lors de la création
                                                */
            ~Entreprise();
            /* il sera sans doute utile de récupérer le nom de l'entreprise */
            const std::string& name() const;
            /* elle doit pouvoir engager quelqu'un 
             * l'identifiant du salarie est choisi par l'entreprise
             */
            void engage(const std::string& n, const std::string& p, char s, 
                        unsigned int a);
            /* elle doit pouvoir afficher tous ses salariés */
            void afficheTous() const;
            /* et elle doit pouvoir les enregistrer dans un fichier
             * dont on donne le nom en parametre 
             */
            void enregistre(const std::string& filename) const;
        private:
            /* le nom de l'ntreprise */
            std::string mname;
     
            /* les salariés qu'elle emploie */
            std::list<Salarie> salaries;
    };
    L'implémentation des différentes méthodes de l'entreprise prendrait 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
    /* pour le constructeur, utilisons la liste d'initialisation */
    Entreprise::Entreprise(const std::string& n):mname(n),salarie(){}
    /* le destructeur n'a rien à faire */
    Entreprise::~Entreprise(){}
    /* L'entreprise doit répondre à la question "quel est ton nom" */
    const std::string& Entreprise::name() const { return mname;}
    /* L'entreprise doit engager un salarié
     * @ params : n: le nom du salarie
     *            p: le prénom du salarie
     *            s: le sexe du salarie
     *            a: l'age du salarie
    */
    void Entreprise::engage(const std::string& n, const std::string& p, char s, 
                        unsigned int a)
    {
        /* création du salarié, l'identifiant est calculé sur le nombre 
         * de salariés déja existants
         */
        Salarie toadd={salaries.size()+1,n,p,s,a};
        /* ajout du salarié à la fin de la liste */
        salaries.push_back(toadd);
    }
    /* l'affichage des salariés à l'écran */
    void Entreprise::afficheTous() const
    {
         /* parcourrons la liste des salariés */
        for(std::list<Salarie>::const_iterator it= salaries.begin();
           it!=salaries.end();++it)
        {  
            /* et demandons en l'affichage */
            std::cout<<(*it).nom<<" "<<(*it).prenom<<" "<<(*it).age
                     <<" "<<(*it).sexe<<std::endl;
        }
    }
    /* l'enregistrement des salariés dans un fichier dont le nom est
     * donné en parametre
     */
    void Entreprise::enregistre(const std::string& filename) const
    {
        /* ouvrons un fichier en lecture seule */
        std::ofstream ofs(filename.c_str());
         /* parcourrons la liste des salariés */
        for(std::list<Salarie>::const_iterator it= salaries.begin();
           it!=salaries.end();++it)
        {  
            /* et demandons l'écriture dans le ficheir */
            ofs<<(*it).nom<<" "<<(*it).prenom<<" "<<(*it).age
                     <<" "<<(*it).sexe<<std::endl;
        }
    }
    L'utilisation se ferait 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
    12
    13
    14
    int main()
    {
        /* créons une entreprise pour gérer les salariés */
        Entreprise ent("mon entreprise a moi");
        /* demandons lui d'engager du personnel */
        ent.engage("MainALaPate","Koala",'M',36);
        ent.engage("Doe","Jhonn",'M',42);
        /* demandons lui d'afficher les salariés */
        ent.affichTous();
        /* et de les enregistrer dans un fichier */
        ent.enregistre("test.txt");
        /* on a fini, on quitte normalement l'application */
        return 0;
    }
    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 confirmé

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2007
    Messages
    177
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2007
    Messages : 177
    Par défaut
    Bonjour koala01! Merci de m'avoir répondu. Avec ce que tu viens de me dire, tu m'éloignes du c standard pour me rapprocher du c++ (ce qui me convient parfaitement). C'est vrai que je n'avais pas penser à utiliser les entiers non-signés et que c'est un tort de ma part.

    Exemple que tu m'as donnée est bien, même très bien. Mais mon but, c'est de les stockés dans un tableau qui regroupent tous les salariés et non de les faire un par un. Comme ça, je peux faire une dll qui me permettra de gérer tout ça.

    Et je voudrais l'initialiser comme ceci avec la reprise de ton exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    void EngagerSalarie(const Salarie& salarie);
    Mais je n'y arrive pas à faire un tableau de ce type! Comment faire un tableau et l'insérer dans EngagerSalarie(const Salarie& salarie);

    Et il y a une chose que je voudrais savoir, A quoi sert le "&" dans "Salarie&"?

    Merci encore pour les astuces que tu m'as apportés, comme pour les différent fichier d'en-tête ou pour l'affichage avec std::cout, car j'essaie de me rapproché le plus possible du c++.

  4. #4
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 641
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 641
    Par défaut
    Ce qu'il faut comprendre, c'est que si tu as un tableau de salariés, c'est que, l'ordre logique dans la vie de ton application sera:
    1. L'utilisateur introduit les premiers salariés (les crées), en répondant à des questions (en mode "console"), ou en remplissant "un certain nombre de fois" un formulaire d'ajout de salarié
    2. L'application enregistre les salariés dans un fichier à certains moments clés (sur demande de l'utilisateur, ou au plus tard, avant de quitter l'application)
    3. Lorsque l'application est relancée, elle charge (éventuellement) les salariés sauvegardés, et le cycle recommence

    (ou alors, nous ne nous comprenons pas sur le sens de ce que devrait faire EnregistreSalarie)

    Je parle, ici d'application, de manière générale, mais c'est tout à fait applicable au "cycle d'utilisation" de ta dll, tout comme ce serait applicable au "cylce d'utilisation" d'une bibliothèque statique

    Tu ne peux effectivement pas enregsitrer quelque chose... d'inexistant parce que cela ne sert à rien, et que l'on serait en droit de se poser la question de savoir... quelles seront les données sauvgardées

    Il faudra donc régulièrement pouvoir créer un salarié unique et le rajouter "à ceux qui existent", même s'il faut d'un autre coté pouvoir les gérer "de manière groupée".

    La classe que j'ai présentée sous le nom Entreprise est - bien sur - modifiable à souhait, selon tes besoins réels, mais elle représente une chose indispensable: la collection qui a en charge la gestion des salariés.

    Et la responsabilité de la gestion des salariés implique qu'elle soit au minium capable de créer de nouveaux salariés, de les lire depuis un fichier et de les enregistrer

    Maintenant, s'il ne faut qu'une seule collection de salariés, existant du début à la fin du programme, et / ou qu'il n'est pas nécessaire de pouvoir donner un nom à la collection, la classe peut être renommée en un terme plus générique comme "SalarieHolder", le membre mname peut etre supprimé (sans oublier de supprimer l'accesseur const std::string& name() const) et la classe peut être transformée en singleton, mais sa responsabilité restera identique et donc, les méthodes qu'elle devra fournir aussi

    Sois en outre attentif au fait que, si tu pars du principe de la création d'une bibliothèque partagée / dynamique, il redevient alors nécessaire de s'assurer que tout ce qui est accessible depuis l'extérieur ne manipule (en apparence du moins) que des types primitifs afin d'avoir la certitude que ta dll compilée (par exemple) avec gcc soit utilisable sous VC++ , mais cela n'impliquera que la modification de quelques prototypes
    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

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 641
    Par défaut
    j'ai failli passer outre la question:
    Citation Envoyé par terminator59140 Voir le message
    <snip>
    Et il y a une chose que je voudrais savoir, A quoi sert le "&" dans "Salarie&"?
    Le & quand il est utilisé pour la déclaration (d'une variable ou d'un argument) signale que nous utilisons une référence sur l'objet.

    Autrement dit, nous utilisons "un alias" de l'objet réel, ce qui nous permet d'éviter les copies inutiles.

    A titre de comparaison, c'est fort semblable au fait de passer un pointeur sur un objet de type Salarie en C, avec la principale différence que l'objet ne peut pas ne pas exister.

    Dés que tu dois transmettre en paramètre des valeurs qui ne sont pas des types primitifs, il est préférable d'utiliser des références pour éviter qu'il n'y ait création forcée d'une variable du type adéquat propre à la fonction appelée qui serait... la copie de la variable passée en paramètre.

    En effet, si tu venait à prévoir une fonction sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void foo(Salarie s)
    {
        /*utilisation de s */
    }
    s serait une variable propre à foo, qui serait une copie de la variable utilisée lors de l'appel.

    cela signifie que s sera créé de toute pieces spécialement pour l'appel, et qu'il y aura un mécanisme de copie de l'ensemble de son contenu qui sera mis en oeuvre.

    Cette copie va prendre d'autant plus de temps et de ressources (au niveau de la mémoire) que le contenu est important, et donc, si l'on peut estimer que la copie de deux entiers, deux chaines de caractères et d'un caractère ne va peut etre pas utiliser énormément de ressources, imagine ce qu'il en serait si tu venais à demander la copie de la variable de type Entreprise (pour rester proche de l'exemple) quand elle contient, mettons, 10000 salariés

    Comme cela revient en fait à permettre à la fonction appelée de travailler avec la variable qui existe dans la fonction appelante (et qui a été donnée en paramètre), la bonne habitude à prendre est de la passer sous forme constante si la fonction appelée ne doit pas être en mesure de la modifier
    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

Discussions similaires

  1. Problème de tableau de structures, étrange
    Par Kalyptus dans le forum Débuter
    Réponses: 15
    Dernier message: 08/06/2008, 01h05
  2. Problème de tableau dans une structure
    Par zarbiman dans le forum Réseau
    Réponses: 5
    Dernier message: 15/12/2007, 18h21
  3. Problème avec un tableau de structure
    Par Sofute dans le forum C
    Réponses: 10
    Dernier message: 16/10/2007, 15h29
  4. Problème free() : Tableau de structures
    Par bit_o dans le forum C
    Réponses: 11
    Dernier message: 28/04/2007, 15h53
  5. Réponses: 7
    Dernier message: 21/12/2005, 16h44

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