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 :

Comptes bancaires & Clients


Sujet :

C++

  1. #1
    Membre régulier

    Profil pro
    Étudiant
    Inscrit en
    Mars 2013
    Messages
    118
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2013
    Messages : 118
    Points : 81
    Points
    81
    Par défaut Comptes bancaires & Clients
    Bonjour,

    J'ai une classe CompteB et une classe ClientMono qui représente une client possédant un seul compte en banque.

    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
    #ifndef COMPTEB_H
    #define COMPTEB_H
    #include <string>
    using namespace std;
     
    class CompteB {
     private :
      int numero;
      float solde;
     
     public :
      CompteB(int);
      void crediter(float);
      void debiter(float);
      int getNumero() const;
      void setNumero(int n);
    };
     
    #endif
    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
    #include "CompteB.h"
    using namespace std;
     
    CompteB::CompteB(int n): numero(n), solde(0) {}
     
    void CompteB::crediter (float f) {
      solde += f;
    }
     
    void CompteB::debiter (float f) {
      solde -= f;
    }
     
    int CompteB::getNumero() const {
      return numero;
    }
     
    void CompteB::setNumero(int n) {
      numero = n;
    }
    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
    #ifndef CLIENTMONO_H
    #define CLIENTMONO_H
    #include <string>
    #include "CompteB.h"
    using namespace std;
     
    class ClientMono {
     
     private:
      string nom;
      CompteB compte;
     
     public:
      ClientMono();
      ClientMono(string);
      ClientMono(string, CompteB);
      string getNom() const;
      CompteB getCompteB() const;
     
    };
     
    #endif
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include "ClientMono.h"
    #include <iostream>
    #include <string>
     
    using namespace std;
     
    ClientMono::ClientMono(): nom(" ") {}
     
    ClientMono::ClientMono(string s): nom(s) {}
     
    ClientMono::ClientMono(string s, CompteB c): nom(s), compte(c) {}

    Malheureusement, j'ai une liste d'erreur longue comme le bras a la compilation de ClientMono.cpp.

    ClientMono.cpp: In constructor ‘ClientMono::ClientMono()’:
    ClientMono.cpp:8:34: error: no matching function for call to ‘CompteB::CompteB()’
    ClientMono.cpp:8:34: note: candidates are:
    In file included from ClientMono.h:6:0:
    CompteB.h:15:3: note: CompteB::CompteB(int)
    CompteB.h:15:3: note: candidate expects 1 argument, 0 provided
    CompteB.h:8:7: note: CompteB::CompteB(const CompteB&)
    CompteB.h:8:7: note: candidate expects 1 argument, 0 provided
    ClientMono.cpp: In constructor ‘ClientMono::ClientMono(std::string)’:
    ClientMono.cpp:10:40: error: no matching function for call to ‘CompteB::CompteB()’
    ClientMono.cpp:10:40: note: candidates are:
    In file included from ClientMono.h:6:0:
    CompteB.h:15:3: note: CompteB::CompteB(int)
    CompteB.h:15:3: note: candidate expects 1 argument, 0 provided
    CompteB.h:8:7: note: CompteB::CompteB(const CompteB&)
    CompteB.h:8:7: note: candidate expects 1 argument, 0 provided
    Quelqu'un peut me dire ce qui ne va pas ?

  2. #2
    Membre éclairé

    Homme Profil pro
    Non disponible
    Inscrit en
    Décembre 2012
    Messages
    478
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Non disponible

    Informations forums :
    Inscription : Décembre 2012
    Messages : 478
    Points : 877
    Points
    877
    Billets dans le blog
    1
    Par défaut
    Bonjour !

    Le constructeur de CompteB attend systématiquement un entier.( CompteB(int); )Cette déclaration dans ClientMono.h demande à ce qu'il y ai un constructeur par défaut de CompteB.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class CompteB {
    private :
      CompteB() {}//Constructeur par défaut
      CompteB(int);
      ...
    };
    Ou bien rendre le paramètre facultatif :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class CompteB {
    private :
      CompteB( int = 0 );//Si il n'y a pas de paramètre, sa valeur sera 0
      ...
    };

  3. #3
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Ou utiliser la liste d'initialisation
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  4. #4
    Membre éclairé

    Homme Profil pro
    Non disponible
    Inscrit en
    Décembre 2012
    Messages
    478
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Non disponible

    Informations forums :
    Inscription : Décembre 2012
    Messages : 478
    Points : 877
    Points
    877
    Billets dans le blog
    1
    Par défaut
    Mais il faut utiliser un objet par défaut ?!

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ClientMono(std::string nom = std::string(), CompteB compte = CompteB(-1)): nom(nom), compte(compte) {}//-1 numero par défaut
    Pour passer l'objet en référence constante ça devient (un peut) plus compliqué non ?

  5. #5
    Membre régulier Avatar de GrosLapin
    Homme Profil pro
    Ingénieur et Etudiant
    Inscrit en
    Avril 2013
    Messages
    47
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur et Etudiant

    Informations forums :
    Inscription : Avril 2013
    Messages : 47
    Points : 85
    Points
    85
    Par défaut
    Tu peux également te demander si ça un sens d'avoir deux comptes bancaire avec le même numéro.

    Si ce n'est pas le cas (à mon avis), le fait de laisser le choix à l’extérieur de la classe est dangereux car cella permet d’écrire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    CompteB (1) ComptePersonne1;
    ComptePersonne1.crediter(42);
    CompteB (1) ComptePersonne2;
    Bien que cela ne créera pas d'erreur, ce comportement est différent de la réalité que tu essais de représenter, puisque deux comptes on le même numéro (déjà ça c'est étrange) avec un solde différent.

    Si tu considère que chaque CompteB doit avoir un numéro unique, tu n'as plus besoin de paramètre à ton constructeur et ton code compilera.

    Il te faudra ensuite trouver un moyen d'assurer un numéro unique à chaque instance (tu peux chercher de ce côté ou de celui là si tu compte mettre de l’héritage). Cela te permettra également, comme la sous entendu PilloBuenaGente, de te rendre compte que tu crées beaucoup de copie de CompteB.

    Apres je peux me tromper et tu peux avoir choisi ce comportement volontairement.

  6. #6
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Hello,

    Comme dit, le constructeur de CompteB prend un int en paramètre (le constructeur vide par défaut n'existe pas car tu défini un constructeur perso).

    Pense cependant à le mettre explicit, ça t'éviteras des conversion int -> CompteB non voulues.

  7. #7
    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,
    Citation Envoyé par PilloBuenaGente Voir le message
    Mais il faut utiliser un objet par défaut ?!

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ClientMono(std::string nom = std::string(), CompteB compte = CompteB(-1)): nom(nom), compte(compte) {}//-1 numero par défaut
    Pour passer l'objet en référence constante ça devient (un peut) plus compliqué non ?
    En fait, non...

    Lorsqu'une fonction prend une référence constante (et uniquement lorsque la référence est constante) sur un objet, tu peux parfaitement utiliser ce que l'on appelle une "variable temporaire anonyme".

    Il s'agit de créer un objet (une variable) spécifiquement pour l'occasion (l'appel de la fonction) dont la durée de vie ne sera que celle de l'exécution de la fonction appelée et qui ne sera donc pas accessible (vu qu'il n'y a aucun identifiant pour y accéder ainsi que du fait de sa durée de vie réduite) depuis la fonction appelante.

    C'est, d'ailleurs, pour cette raison que dans "leur infinie sagesse" les responsables de la norme autorisent un tel comportement uniquement pour les références constantes, en estimant qu'il n'y a aucune raison d'en permettre la modification si... ces modifications ne peuvent pas être récupérées au niveau de la fonction appelante

    Par contre, si l'on décide de transmettre un objet existant par ailleurs, le fait qu'il s'agisse d'une référence (constante, pour l'occasion) évite qu'il n'y ait copie de l'objet.

    Et comme la copie d'un objet peut demander énormément de ressources, en termes de mémoire comme en terme de temps (sans oublier le fait que certaines classes n'ont pas vocation à être copiées), cela permet de gagner énormément de performances "à peu de frais"

    Le fait de fournir une valeur par défaut dans ce cas est, non seulement légal, mais en outre totalement justifié car cela permet de disposer d'office d'une variable temporaire non nommée même si l'utilisateur n'en fournit pas

    Mais, justement, en parlant des classes qui n'ont vocation à être copiées...

    @Jéjé34>> J'estime personnellement que ta classe CompteB fait partie de cette catégorie de classe, et que tu pars donc avec un très net problème de conception

    En effet, un compte bancaire est, typiquement, unique et non ambigu: tu ne serais surement pas particulièrement content si, parce qu'un autre client disposait d'un compte bancaire portant le même numéro que le tien, les payement effectués par cet autre client étaient... débités de ton propre compte

    Il faut donc pouvoir garantir le fait que chaque numéro de compte n'existe qu'une et une seule fois!

    De plus, le client ne dispose que d'un droit "d'utilisation" du compte bancaire: le compte ne lui appartient pas à proprement parler, mais appartient à... la banque qui le met à disposition du client.

    Il n'appartient donc au client ni de créer le compte, ni de le détruire, mais bel et bien... à la banque qui met le compte à disposition du client.

    Enfin, on se rend compte qu'il peut exister différents types de comptes, qui permettent tous les opérations de crédit et de débit, comme les comptes "courent" ou les comptes d'épargne ("livrets verts" en france ).

    En outre, il n'y a strictement rien qui empêche un seul et même client d'avoir plusieurs comptes, de nature potentiellement différentes (ou potentiellement identiques ) et de peut etre dans différentes banques, tout comme il n'y a strictement rien qui oblige les client à disposer d'un compte en banque (bon, hormis la facilité que cela représente ).

    Cerise sur le gâteau, le mutateur sur le numéro de compte est totalement aberrant : une fois qu'un compte bancaire a été créé, il dispose d'un numéro qui lui est propre et qu'il gardera d'office jusqu'à sa destruction: c'est, en dehors de l'adresse mémoire à laquelle il se trouve (mais sur laquelle on n'a strictement aucune espèce d'influence) le seul et unique moyen de l'identifier de manière "unique et non ambigüe et donc d'éviter le problème que j'exposais plus haut

    Toute ta conception devrait prendre ces différents aspects en ligne de compte

    Par exemple, je t'ai dit qu'il n'est pas bon de pouvoir copier un objet de type CompteB...

    L'idéal est donc d'en empêcher la copie et l'affectation.

    Ta classe CompteB devrait donc ressembler à (j'en profites pour supprimer le setNumero que je ne saurais voir )
    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
     
    /** avant C++11, nous aurions fait comme ceci */
    class CompteB{
        public:
            CompteB(int numero):numero_(numero), solde_(0.0){}
            void crediter(float);
            void debiter(float);
            int numero() const;
            float solde() const;
        private:
            /* le fait de déclarer SANS LES DEFINIR le constructeur par copie
             * et l'opérateur d'affectation dans l'accessibilité nous empêchera d'y
             * avoir recours en dehors des fonctions membres de CompteB
             */
             CompteB(CompteB const &);
             CompteB & operator=(CompteB const &);
             int numero_;
             float solde_;
    };
    /* En C++11, on préférera faire comme cela */
    class CompteB{
        public:
             CompteB(CompteB const &) = delete ; //indique au compilateur que le constructeur par copie n'existe pas
             CompteB & operator=(CompteB const &) = delete ; // indique au compilateur que l'opérateur d'affectation n'existe pas
            CompteB(int numero):numero_(numero), solde_(0.0){}
            void crediter(float);
            void debiter(float);
            int numero() const;
            float solde() const;
        private:
             int numero_;
             float solde_;
    };
    Je t'ai aussi dit que le client n'était que "l'utilisateur" du compte, mais que c'était la banque qui en était propriétaire, et que c'était la banque qui avait le droit de créer (ou de détruire) un compte bancaire.

    Il te faut donc une classe (Banque ) qui s'occupe de gérer les différents comptes bancaires

    Elle prendrait donc 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
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    class Banque{
        public:
            Banque(){
                /* voyons large : disons que chaque banque dispose d'office
                 * de 1000 comptes :D
                 */
                for(int i = 0; i< 1000; ++i){
                    comptes_[i] = new CompteB(i);
                }
            }
            ~Banque(){
                for(auto it = comptes_.begin(); it!= comptes_.end();++it){
                    delete it.second;
                }
            }
            /* elle doit pouvoir fournir les comptes bancaires */
            CompteB * find(int numero) const {
                auto it = comptes_.find(numero);
                if(it!= comptes_.end())
                    return it.second;
                return nullptr;
            }
        private:
            std::map<int, CompteB *> comptes_;
    };
    NOTA : si j'utilise des pointeurs ici, c'est parce que j'ai explicitement précisé qu'un compte bancaire ne pouvait pas être copié... L'utilisation de pointeur est donc la seule solution qui reste pour pouvoir les placer dans une collection (de toutes manières, comme c'est la banque qui les crée et qui les détruit, on ne risque que peu de choses, tant que la banque continue d'exister )

    Enfin, je t'ai dit que le client pouvait ne pas avoir de compte bancaire (tout comme il peut en avoir plusieurs ).

    L'utilisation de pointeurs présente de nombreux avantages dans le cas présent:
    D'abord, ils permettent de représenter le fait qu'un client n'a pas de compte bancaire lorsque le pointeur est à null (nullptr).

    Ensuite, j'ai cité le fait qu'il peut exister plusieurs types de comptes.

    Le fait de passer par un pointeur nous permettra, si tu décides de créer plusieurs classes dérivées de CompteB, de gérer "n'importe quel type particulier" de compte bancaire de la même manière et de profiter, le cas échéant, du polymorphisme

    Ta classe Client pourrait donc ressembler à 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
    class ClientMono {
     
     private:
      string nom_;
      CompteB * compte_;
     
     public:
      ClientMono(string const & nom ):nom_(nom),compte_(nullptr){}
      ClientMono(string, CompteB * compte):nom_(nom),compte_(compte){}
      const string & getNom() const;
      CompteB * getCompteB() const;
      /* nous pourrions d'ailleurs rajouter une fonction qui nous indique si 
       * le client dispose d'au moins un compte : 
       */
      bool aUnCompte() const{return compte_!= nullptr;}
    };
    Enfin, il peut être intéressant de pouvoir récupérer le client au départ d'un compte, tout comme il est intéressant de récupérer un compte depuis le client.

    Cela peut, entre autres, nous permettre de déterminer si un compte donné est... relié à un client ou s'il est disponible pour être "fourni" à un nouveau client

    Évidemment, seule la banque est en droit d'associer un compte à un client, et encore: à condition qu'il ne soit associé à aucun autre client

    Il serait donc intéressant de modifier une dernière fois la classe CompteB pour prendre cet aspect en compte, 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
    class CompteB{
        public:
             CompteB(CompteB const &) = delete ; //indique au compilateur que le constructeur par copie n'existe pas
             CompteB & operator=(CompteB const &) = delete ; // indique au compilateur que l'opérateur d'affectation n'existe pas
            CompteB(int numero):numero_(numero), solde_(0.0), client_(nullptr){}
            void crediter(float);
            void debiter(float);
            int numero() const;
            float solde() const;
            Client* client() const{return client_;}
        private:
             int numero_;
             float solde_;
             /* donne un accès illimité à la banque aux membres privés de la classe
              * il faudra cependant veiller à ce que la banque ne modifie que le pointeur
              * client_ et uniquement dans certaines circonstances ;)
              */
             friend class Banque; 
             Client * client_;
    };
    Et, bien sur, il faut donner la possibilité à la banque d'assigner un client à un compte qui n'est pas encore assigné. Il faudra donc modifier la classe banque 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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    class Banque{
        public:
            Banque(){
                /* voyons large : disons que chaque banque dispose d'office
                 * de 1000 comptes :D
                 */
                for(int i = 1; i< 1001; ++i){
                    comptes_[i] = new CompteB(i);
                }
            }
            ~Banque(){
                for(auto it = comptes_.begin(); it!= comptes_.end();++it){
                    delete it.second;
                }
            }
            /* elle doit pouvoir fournir les comptes bancaires */
            CompteB * find(int numero) const {
                auto it = comptes_.find(numero);
                if(it!= comptes_.end())
                    return it.second;
                return nullptr;
            }
            CompteB* assigneAUnClient(Client * cl){
                /* on assigne le client au premier compte disponible */
                for(auto it : comptes_){
                    if(it->client()!= nullptr){
                        it->client_ = cl;
                        return it;
                    }
                }
                /* oups, si on arrive ici, il n'y a plus de compte disponible...
                 * créons en un pour l'occasion 
                 */
               int newCompte = comtes_.size() +1; //ben oui, j'ai commencé à les numéroter à 1 ;)
               comptes_[newCompte] = new CompteB(newCompte);
               comptes_[newCompte]->client_=cl;
               retrun comptes_[newCompte];
            }
        private:
            std::map<int, CompteB *> comptes_;
    };
    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

  8. #8
    Membre régulier

    Profil pro
    Étudiant
    Inscrit en
    Mars 2013
    Messages
    118
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2013
    Messages : 118
    Points : 81
    Points
    81
    Par défaut
    Merci pour vos réponses !

    En effet, c'est bien le constructeur vide qu'il manquait...

    Je te réponds dans la soirée koala01.

Discussions similaires

  1. VCS - N° de compte bancaire
    Par Tragnee dans le forum Algorithmes et structures de données
    Réponses: 2
    Dernier message: 01/03/2007, 15h48
  2. [VBA-E]Projet de compte bancaire personnel
    Par thibaut_chaps dans le forum Macros et VBA Excel
    Réponses: 8
    Dernier message: 21/12/2006, 23h41
  3. Réponses: 5
    Dernier message: 31/05/2006, 20h06
  4. Algorithme [Gestion d'un compte bancaire]
    Par Laeticia dans le forum Algorithmes et structures de données
    Réponses: 4
    Dernier message: 04/02/2005, 10h57
  5. [Modèle Relationnel] Gestion de comptes bancaires.
    Par Elmilouse dans le forum Schéma
    Réponses: 3
    Dernier message: 31/08/2004, 16h08

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