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 :

vector de classe


Sujet :

C++

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Février 2008
    Messages
    36
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 36
    Points : 18
    Points
    18
    Par défaut vector de classe
    Bonjour,

    Mon programme est un serveur gérant des connections clients demandant à utiliser des requêtes sur un autre serveur.
    Pour ce j'ai créé une classe de clients nommé CClient contenant l'adresse IP du client, un port de connection et un timestamp (date jusqu'a laquelle il peut utiliser les requêtes)
    J'ai une autre classe PoolClient qui est un vector de CClient (en gros).
    Dans le main je déclare un pointeur pListeClient et j'utilise une méthode addClient (déclaré et développé dans PoolClient.cpp et PoolClient.h) pour rajouter le client qui se connecte.
    Le problème que je rencontre est qu'en débug, lorsque je passe dans la méthode addClient(), la liste est bien mise à jour mais quand je ressors de la méthode et que je reviens dans mon main, ce client n'est pas rajouté dans le pointeur pListe Client.
    Est-ce que quelqu'un a déja rencontré ce pb?

    Voici le code utilisé :
    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
     
        int res=treatToken();
        int validity=controlValidity();
        cleanPoolClient();
        if (iperfPort=="")
        {
          iPort = pPortManager->reservePort();
        }
        else
        {
          iPort = pPortManager->reservePort(strToInt(iperfPort));
        }
        time_t tTimestamp = strToInt(timestamp);
        CClient client(addIPappelant, typeconnect, iPort, tTimestamp);
        string IP=client.getIpAddress();
     
        bool iControlIP=controlIP(IP);
        if (iControlIP==true)
        {
          res=6;
        }
        else
        {
        int iControlIperf=controlIperf();
        int ajout=pPoolClient->addClient(client);
        }
        iperfPort="";
    la méthode du PoolClient.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    int PoolClient::addClient(CClient client)
    {
      liste_client.push_back(client);
      int res=liste_client.size();
      return res;
    }
    PoolClient.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
    23
    24
    25
    26
    27
    28
    29
    30
    31
     
    #ifndef POOLCLIENT_H
    #define POOLCLIENT_H
     
     
    #include <iostream>
    #include <algorithm>
    #include <vector>
     #include <ctime>
     
    #include "CClient.h"
     
    using namespace std;
     
    class PoolClient
    {
     
    public:
        PoolClient();
        ~PoolClient();
        int addClient(CClient client);
        int deleteClient(CClient client);
        int cleanPool();
        bool existeIP(string IP);
     
    private:
        vector<CClient> liste_client;
        int deleteClient(vector<CClient>::iterator i);
    };
     
    #endif // POOLCLIENT_H

  2. #2
    Membre éprouvé
    Avatar de mitkl
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2010
    Messages
    364
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Février 2010
    Messages : 364
    Points : 1 081
    Points
    1 081
    Par défaut
    Attention, ça sent l'ancien développeur Java, quand tu fais

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int PoolClient::addClient(CClient client)
    {
      liste_client.push_back(client);
      int res=liste_client.size();
      return res;
    }
    tu traites ici une copie de ce que tu as envoyé à ta fonction membre addClient, par défaut les objets ne font pas envoyés en référence en C++.
    Si vous ne savez toujours pas ce qu’est la récursivité, relisez cette phrase.

    Mon blog sur la programmation et l'informatique !

  3. #3
    Membre à l'essai
    Profil pro
    Inscrit en
    Février 2008
    Messages
    36
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 36
    Points : 18
    Points
    18
    Par défaut
    Ca fait longtemps que je n'ai pas fait d'objet mais en gros je devrais plutot pointé un pointeur CClient?
    Ce que je comprend pas, c'est qu'en débug, mon CClient contient bien les valeurs voulus et les stocke bien dans ma listeClient, c'est uniquement lorsque je sors de la fonction addClient (dans PoolClient) que le pointeur pPoolClient ne contient pas mon client

  4. #4
    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,

    A vrai dire, si tu ne fais pas de distinction entre les clients (comprend: si tu n'as pas de raison de faire dériver ta classe CClient en CClientMachinChose et CClientMachinBidule), il n'y a pas de raison d'utiliser des pointeurs.

    Par contre, tu n'as pas forcément besoin de créer ton client avant de le transmettre à ton pool!

    Je m'explique... :
    Il existe ce que l'on appelle la "loi demeter" qui nous dit, pour faire simple, que si une classe A utilise une classe B et que la classe B utilise elle-même la classe C, la classe A ne doit pas avoir à connaitre la classe C pour manipuler la classe B

    Comme il existe aussi le principe de la délégation des responsabilités (chaque classe ne doit avoir qu'une responsabilité, mais elle doit veiller à bien la prendre en charge), on peut dire que la gestion des nouvelles connexions devrait... être prise en charge par une classe "à part".

    Cette classe n'a strictement aucun besoin de connaitre la notion de "client" : elle sait détrminer l'adresse IP, le port et la date d'expiration de la nouvelle connexion et peut très bien les fournir "en vrac" à ton "PoolCient" qui se chargera d'enregistrer ces informations sous la forme d'un nouveau client qui sera ajouté à la liste des clients actifs.

    Le système qui reçoit les requètes (qui n'a, a priori, rien à voir avec celui d'enregistrement des nouveaux clients, même si cela peut etre associé à une requete particulière) n'a pas d'avantage besoin de connaitre la notion de client.

    En effet, il va recevoir la requete, peut etre un identifiant de session et... l'adresse ip de celui qui envoie la requète.

    Tout ce qu'il doit faire, c'est demander au PoolClient de déterminer, sur base de l'ip et de l'identifiant de session, s'il peut "faire passer" la requête ou s'il dit la bloquer.

    On peut imaginer rajouter un foncteur qui se charge simplement de vérifier les sessions valides...

    Il n'a aucun besoin de la notion de client: tout ce dont il a besoin, c'est d'une date d'expiration et de la date actuelle

    Enfin, même s'il y a un système qui se charge périodiquement de supprimer les sessions expirées, il n'a absolument pas besoin de la notion de client : il faut juste qu'il puisse accéder à une liste d'identifiant de session et à la date d'expiration au départ d'un identifiant de session, de manière à ce qu'il puisse indiquer, si le foncteur dont j'ai parlé plus haut lui dit que la session à expiré, au PoolClient de supprimer la session identifiée par... l'identifiant de session.

    Je pourrais continuer comme cela longtemps en te parlant d'un système gardant les historiques de session, encore de plein de chose...

    Ce qui importe dans ce que j'essaye de t'expliquer, c'est qu'il faut impérativement essayer de respecter ce principe d'une classe, une responsabilité.

    La notion de client n'est qu'un ensemble de données qui vont très bien ensembles, mais que l'on n'utilise jamais toutes en même temps et peut donc parfaitement être encapsulée dans ton PoolClient de telle manière à ce qu'aucune classe ayant affaire au PoolClient ne doive même savoir qu'il existe une classe... CClient.

    La raison pour laquelle j'explique tout cela peut parraitre floue, et je vais donc conclure en l'expliquant

    J'ai indiqué, en commençant mon intervention, en te disant qu'il n'y avait a priori pas de raison d'utiliser des pointeurs, et je viens d'écrire un roman pour justifier le fait qu'aucune classe (hormis PoolClient) n'a jamais réellement besoin de connaitre la notion de Client.

    Il faut savoir que les collections de la STL nécessitent toutes que les objets que l'on y introduit soient copiables.

    Or, la notion de client en elle-même tend à donner une sémantique d'entité à la classe CClient de par "l'unicité référencielle" qu'elle représente : un client est (essentiellement) identifié par son identifiant de session, et, si on a deux fois le même identifiant, c'est qu'il s'agit à l'évidence du même client.

    Pour notre malheur, une classe ayant sémantique d'entité est généralement non copiable, justement parce que nous ne pourrions plus assurer "l'unicité référencielle" si deux objets se trouvant à des adresses différentes en mémoire venaient à avoir le même identifiant de session.

    Nous devrions donc sans doute, pour cette raison, nous orienter vers le maintien d'une collection de pointeurs sur CClient associé à toute la partie désagréable de la gestion dynamique de la mémoire allouée à ces pointeurs (ou associé à l'utilisation des pointeurs intelligents)...

    Par contre, on peut estimer que si le seul moment où l'unicité référencielle n'est pas respectée, ce sont ces quelques cycles nécessaires à l'ajout d'un nouveau client dans la collection (parce que, encore une fois, il n'y a vraiment que PoolClient qui doive avoir connaissance de la notion de client !!! ) il peut devenir intéressant de "glisser" sur le fait qu'une classe ayant sémantique d'entité n'est pas copiable.

    Au final, on pourrait très bien avoir un code ressemblant à quelque chose comme
    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
    class PoolClient
    {
        /* la classe Client est imbriquée dans l'accessibilité privée de PoolClient
         * elle n'est donc pas accessible en dehors ;)
         */
        struct Client
        {
            Client(IP const & ip, Port const & port, SessionId const & session,
                     Date const & expire):
                  ip(ip),port(port),id(session), expire(expire){}
            IP ip;
            Port port;
            SessionId id;
            Date expire;
        };
        public:
            /* on doit pouvoir rajouter un client */
            void add(Ip const & ip, Port const & port)
            {
                allClients.push_back(Client(ip,port,createSession(),calculateExpiration());
            } 
            /* on doit pouvoir supprimer un client au départ de son identifiant de session */
            void remove(SessionId const & id)
            {
                /* recherche du client correspondant et suppression de l'élément */
            }
            /* on doit pouvoir récupérer un identifiant au départ d'une ip */
            SessionId const & idByIp(Ip const & ip) const
            {
                /* recherche de du client dont l'ip est ip et renvoit de son identifiant 
                 * de session
                 *
                 * renvoi d'un identifiant "invalide" si le client n'est pas enregistré
                 */
            }
            /* on doit pouvoir obtenir la date d'expiration au départ de l'identifiant de session */
          Date const & expiration(SessionId const & id) const
          {
              /* recherche du client correspondant renvoi de sa date d'expiration
               * renvoi d'une date identifiée comme invalide si le client n'existe pas */
          }
          /* on a besoin de pouvoir remplir une collection d'identifiants avec
           * tous les identifiants des clients enregistrés
           */
          void fillIdList(std::list<SessionId> & list) const
          {
              list.clear();
              /* pour chaque client, ajouter l'identifiant à la liste */
           }
        private:
            /* en interne uniquement, on doit pouvoir retrouver un client
             * sur base de son identifiant
             */
           Client const & find(SessionId const & id ) const
           {
               /*...*/
           }
           /* en interne seulement, on doit pouvoir générer un identifian de session
            * qui sera unique
            */
            SessionId createSession() const
            {
                 /* ... */
            }
            /* en interne seulement, on doit pouvoir générer une date d'expiration */
           Date calculateExpiration() const 
           {
               /* ... */
           }
           std::vector<Client> allClients;
    }
    j'ai considéré, pour l'exemple, que chaque donnée représentative était un type particulier, mais il y a de fortes chances que ce ne soit pour la plupart à chaque fois que des entiers...

    Si c'es effectivement le cas, il n'est pas nécessaire de les transmettre ni de les renvoyer par référence constante : la copie reste très efficace tant que l'on ne dépasse pas la taille d'un size_t
    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
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Citation Envoyé par freehair Voir le message
    Ca fait longtemps que je n'ai pas fait d'objet mais en gros je devrais plutot pointé un pointeur CClient?
    Ce que je comprend pas, c'est qu'en débug, mon CClient contient bien les valeurs voulus et les stocke bien dans ma listeClient, c'est uniquement lorsque je sors de la fonction addClient (dans PoolClient) que le pointeur pPoolClient ne contient pas mon client
    Si tu modifies ton CClient dans ta fonction, c’est un paramètre in/out. Et donc, il doit être passé par référence.

    Sinon, les modifications que tu fais le sont sur une copie de ton objet original, qui est détruite dès qu’on sort de la fonction, ce qui explique le comportement que tu observes en debug.

Discussions similaires

  1. vector de classe templatée
    Par Lintel-oo dans le forum Langage
    Réponses: 3
    Dernier message: 29/04/2012, 16h59
  2. Vector et classe
    Par sheep_one dans le forum Débuter
    Réponses: 4
    Dernier message: 26/01/2012, 07h47
  3. vector de classe
    Par Invité dans le forum SL & STL
    Réponses: 10
    Dernier message: 23/11/2008, 16h20
  4. Problème avec vector de classe template :(
    Par coyotte507 dans le forum Langage
    Réponses: 7
    Dernier message: 16/04/2008, 12h40
  5. [CONCEPTION] vector de classes heritées
    Par A-S-H dans le forum SL & STL
    Réponses: 9
    Dernier message: 28/12/2005, 09h50

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