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 :

Créer une classe unique par le nom


Sujet :

C++

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2015
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Côte d'Ivoire

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2015
    Messages : 2
    Par défaut Créer une classe unique par le nom
    J'ai une classe dont l'un des attributs est le nom, je voudrais que la création de cette classe se fasse à base de son attribut nom et qu'elle soit unique.

  2. #2
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 759
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 759
    Par défaut
    Il faudra 1 peu + de précision

    Parce que si la classe a un attribut, cela veut dire qu'elle est déjà créée (au moins 1 appel d'1 constructeur) donc impossible d'ajouter des membres ni des méthodes.

    Donc, on peut penser à une initialisation paresseuse ("lazy initialisation") : avec soit des membres dérivants tous d'1 même classe mère, soit des membres de type void* (<- la généricité du C avec la lourdeur que cela engendre)

  3. #3
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2015
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Côte d'Ivoire

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2015
    Messages : 2
    Par défaut
    En fait, après avoir créé la classe dont l'un des attributs est une chaîne de caractère, on me demande dans le menu principal de faire en sorte que lorsque l'utilisateur crée une classe sur la base de son attribut chaîne de caractère que cela soit unique, qu'il n'existe aucune classe qu'on ait créé auparavant qui a ce même attribut chaîne de caractère.

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

    Informations professionnelles :
    Activité : aucun

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

    Le fait est que tu peux empêcher l'utilisateur de ta classe de faire pas mal de choses, comme de copier une instance de ta classe, ou comme assigner les valeurs d'une instance à une autre instance existante, mais tu ne peux pas l'empêcher "comme cela" de créer spécifiquement deux instances de la classe dont l'un des attributs serait identique.

    Laisses mois te montrer:
    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
    //soit la classe 
    class MaClasse{
    public:
        /* je veux que l'utilisateur nous donne le nom sous lequel l'instance sera connue
         */
        MaClasse(std::string const & name);
        /* je veux empêcher l'utilisateur de créer une copie
         */
        MaClasse(MaClasse const &) = delete;
        /* je veux empêcher l'utilisateur d'assigner les valeurs d'une instance à l'autre
         */
        MaClasse & operator=(MaClasse const &) = delete;
         /* Ma classe a visiblement sémantique d'entité.  Comme je ne sais pas si
          * on va créer une hiérarchie de classes, autant prévoir que ce sera fait
          */
        virtual ~MaClasse() = default;
        /* Je veux pouvoir accéder (en lecture seule) au nom */
        std::string const & name() const;
    private:
        /* l'attribut de nom */
        std::string /* const */ name_;
    };
    L'implémentation des fonctions qui sont nécessaires (le constructeur le l'accesseur name) prendrait la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    MaClasse::MaClasse(std::string const & name):name_{name}{
        /* Je n'ai aucun moyen simple, ici, de vérifier si la
         * valeur de name_ n'est pas déjà utilisée par une autre
         * instance de ma classe
         */
    }
    std::string const & MaClasse::name() const{
        return name_;
    }
    Sous cette forme, le compilateur "engueulera" l'utilisateur de la classe s'il essaye de créer une copie ou d'affecter les valeurs d'une instance à une autre instance existante, par exemple, le code qui suit ne compilera pas
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int main(){
        MaClasse martin{"Martin"}; // OK
        MaClasse henry{"Henry"}; // OK
        MaClasse copie{martin}; // essaye de copier l'instance martin: refusé
        martin = henry; // essaye d'assigner les valeurs de henry à celles de martin : refusé
    }
    C'est déjà pas mal, hein?

    Mais, comme je l'ai dit dans le commentaire du constructeur de MaClasse, je n'ai aucun moyen simple de savoir si la valeur transmise en paramètre pour représenter l'attribut name n'a pas déjà été utilisé pour une autre instance. Le code qui vient va donc être accepté et fonctionner (pour notre maleur):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    int maiin(){
        MaClasse martin{"Martin"}; 
        MaClasse autre{"Martin"}; // OOUPSS
        std::cout<<"nom de l'instance martin "<<martin.name()<<"\n"
                 <<"nom de l'instance autre "<<autre.name()<<"\n"; // HEUU... C'est moche...
                                                                   // Car on aurait voulu que le nom "Martin" soit unique
    }
    Alors, bien sur, il existe des solutions. Mais ces solutions ne passent pas par le code de la classe MaClasse. Et cela pour une simple et bonne raison: quand on exécute le constructeur de MaClasse, il est déjà trop tard: la décision de créer une nouvelle instance a déjà été prise,et on ne peut pas revenir en arrière (enfin, si, on pourrait, et de différentes manières d'ailleurs, mais ce ne serait tout simplement "pas logique" ).

    Par contre, rien ne nous empêche de mettre en place une logique qui refuse de créer une nouvelle instance de la classe si les conditions ne sont pas remplies.

    Par exemple, lorsque l'utilisateur veut créer une nouvelle instance, on peut lui demander le nom qui devra être utilisé, et vérifier si ce nom est déjà utilisé par une des instances qui existent déjà. Si le nom n'existe pas, on peut créer la nouvelle instance, et, si le nom existe déjà, on refuse de la créer, et on a sans doute intérêt à afficher un message explicite à l'utilisateur.

    La grosse question sera alors :
    comment savoir si le nom introduit par l'utilisateur existe déjà
    Mais, avant de répondre à cette question, il y en a une autre encore plus importante à se poser:
    comment puis-je faire pour représenter un nombre inconnu à l'avance d'instances de ma classe
    Commençons donc par répondre à la deuxième question:

    Pour pouvoir représenter et maintenir en mémoire un nombre inconnu à l'avance d'instances de MaClasse, nous devrions sans doute utiliser une collection "de taille dynamique".

    La réponse à la première question tombe alors "sous le sens" : Une fois que l'on sait quel nom l'utilisateur veut ajouter, on peut vérifier dans la collection utilisée pour représenter les instances de la classe qui ont déjà été créées si... le nom demandé par l'utilisateur existe déjà Et la logique à mettre en oeuvre pourrait donc prendre la forme de
    1. Demander le nom à ajouter
    2. vérifier si le nom demandé existe déjà
    3. si le nom existe déjà
      • Afficher un message de refus et recommencer en (1)
    4. sinon
      • ajouter une nouvelle instance à la collection

    La collection la plus couramment utilisée pour représenter "un nombre inconnu à l'avance d'éléments " est sans doute la classe std::vector. Elle pourrait très bien faire l'affaire

    Cependant, ce type de collection présente, certes, de nombreux avantages, il n'en demeure pas moins qu'il présente également de nombreux inconvénients. Le principal d'entre eux -- dans le cadre qui nous occupe -- étant la lenteur de la recherche, qui se fait classiquement avec une complexité en O(N) (comprends: il faut, dans le pire des cas, parcourir N des N éléments que std::vector contient pour trouver l'élément que l'on recherche).

    Tu pourrais donc tout aussi bien utiliser des collections qui permettent une recherche beaucoup plus rapide car s'effectuant avec une complexité en O(log(N)); c'est à dire qui permettent de supprimer la moitié des éléments restants à chaque fois que l'on vérifie un élément ne correspondant pas à celui que l'on recherche (on parle de recherche "dichotomique").

    La bibliothèque standard met deux classes de ce genre à notre disposition: la classe std::set et la classe std::map.

    La différence entre les deux étant que chaque élément appartenant à un std::set sert -- de lui-même -- de "clé de tri" pour le std::set. On ne peut donc pas modifier l'élément facilement. Alors que std::map va utiliser une clé (qui permet le tri et qui ne peut donc pas être modifiée) et une valeur (qui peut être modifiée).

    Les deux pourraient s'avérer utiles ici, du fait que ma classe MaClasse est particulièrement minimaliste. Mais si on prévoyait de pouvoir modifier quelques valeurs appartenant à cette classe (à l'exception bien sur de l'attribut name_ ), l'utilisation d'une std::map serait sans doute préférable

    Pour le reste, il y aura quelques fonctions à mettre en place, parmi lesquelles on pourrait citer:
    • afficherMenu : se contente d'afficher un menu offrant le choix de la "prochaine action" à l'utilisateur
    • extraireChoix : récupère le choix de la "prochaine action" introduit par l'utilisateur. Devra s'assurer que ce que l'utilisateur a introduit est "cohérent" avec le choix proposé
    • demanderNom : demande le nom qui devra être utilisé pour la nouvelle instance de la classe
    • nomExiste : vérifie parmi les instances existantes si le nom (introduit par l'utilisateur et récupéré par demanderNom) existe déjà
    • ajouterElement : ajoute une nouvelle instance de la classe, dont le nom correspond au nom introduit par l'utilisateur et récupéré par demanderNom à la liste des instances existantes
    • d'autres fonctions seront sans doute nécessaires

    Mais comme ce serait trop facile si je faisais tout le travail à ta place, je vais te laisser réfléchir à la manière de mettre ces fonctions en place par toi-même

    Après tout, pourquoi devrais-je faire tout le boulot
    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 confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 759
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 759
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Pour pouvoir représenter et maintenir en mémoire un nombre inconnu à l'avance d'instances de MaClasse, nous devrions sans doute utiliser une collection "de taille dynamique".
    J'y aie pensé mais le coup de la collection ne fonctionne pas avec les cas avec différents types (n'ayant aucun héritage en commun), ni sur l'ajout d'attributs membre de différents types (surtout les PODs), ni avec les collections de différents pointeurs de fonction (au alors peut-être utiliser std::function )
    Par exemple :
    • "voiture" -> 4 roues, 1 volant, 4 ou 5 portes - type d'essence, nombre de places occupées (size_t), litre d'essence (float) - conduire, reculer, charger le coffre
    • "moto" -> 2 roues, 1 guidon - litre d'essence (float) - piloter
    • "tank" -> 2 chenilles, 1 poste de pilotage, 1 tourelle - nombre d'obus (size_t), y_a_t_il_un_guetteur (bool) - avancer, tirer, gérer les obus


    Il faut que @Malamba nous renseigne

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 637
    Par défaut
    Oupsss ... Au temps pour moi, j'avais mal interprété le besoin.

    Si le but est de pouvoir créer une instance d'une classe à partir du nom de la classe, il faut partir sur le principe d'une fabrique, qui utiliserait une std::map.

    Mettons donc que l'on ait une hiérarchie de classes 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
    26
    27
    28
    29
    30
    31
    32
    class Vehicule{
    public:
        Vehicule(Vehicule const &) = delete;
        Vehicule & operator=(Vehicule const &)= delete;
        virtual ~Vehicule() = default;
        std::string const & name() const{
            return name_;
        }
    protected:
        Vehicule(std::string const & name):name_{name}{
        }
    private:
        std::string const name_;
    };
    class Voiture : public Vehicule{
    public:
        Voiture():Vehicule{"voiture"}{
        }
        /* ... */
    };
    class Camion : public Vehicule{
    public:
        Camion():Vehicule{"camion"}{
        }
        /* ... */
    };
    class Moto : public Vehicule{
    public:
        Moto():Vehicule{"moto"}{
        }
        /* ... */
    };
    En faisant au plus simple, nous pourrions creer une fabrique 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
    15
    16
    17
    18
    19
    20
    21
    22
    class Fabrique{
    using VehicleMap = std::map<std::string, std::function<Vehicule * (void)>>;
    public:
        /* static*/ Vehicule* creer(std::string const & name){
            static VehicleMap  vehicles;
            if(vehicles.empty())
                initializeVehicles(vehicles);
            if(auto const & toCall = vehicles.find(name); toCall!= vehicles.end()){
                auto * result = toCall->second();
                assert(result!= nullptr && "result is nullpointer");
                return result;
            }
     
            throw std::runtime_error("unable to find requested vehicle name");
        }
    private:
        void initializeVehicles(VehicleMap & v){
            v.insert(std::make_pair("voiture", []()->Vehicule*{return new Voiture;}));
            v.insert(std::make_pair("moto", []()->Vehicule*{return new Moto;}));
            v.insert(std::make_pair("camion", []()->Vehicule*{return new Camion;}));
        }
    };
    qui nous permettrait, par la suite, de créer différentes instances des différentes classes 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
     
    int main(){
        Fabrique fab;
        auto * first = fab.creer("voiture");
        auto * second = fab.creer("moto");
        auto * third = fab.creer("camion");
        std::cout<<"first is a "<<first->name()<<"\n"
                 <<"second is a "<<second->name()<<"\n"
                 <<"third is a "<<third->name() <<"\n";
    }
    Il va bien sur de soi que les clases Voiture, Moto et Camion sont réduites à leur plus simple expression ici, et qu'il faudrait sans doute veiller à ce que la fabrique renvoie des pointeurs intelligents pour éviter tout problème, mais le principe est néanmoins bien là
    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

  7. #7
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 759
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 759
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Si le but est de pouvoir créer une instance d'une classe à partir du nom de la classe, il faut partir sur le principe d'une fabrique
    Relis sa phrase "je voudrais que la création de cette classe se fasse à base de son attribut nom" donc la fabrique et l'instance qu'elle crée sont mélangées ... mais les 2 sont créées l'une après l'autre

    Ce n'est pas une fabrique normale qui retourne un pointeur (intelligent ou pas) c'est une fabrique qui s'auto fabrique (*) d'où ma première question

    (*) Plus précisément, c'est 1 classe qui se crée en 2 fois avec 1 fabrique en 2ième temps

    Mais lorsque tu lis sa deuxième réponse "qu'il n'existe aucune classe qu'on ait créé auparavant qui a ce même attribut chaîne de caractère." on comprend que cette chaîne de caractères est une sorte d'identifiant.
    Pour garantir l'unicité d'1 identifiant c'est 1 tout autre problème, problème trivial, ... mais cela nécessite de connaître l'énoncé exact

Discussions similaires

  1. Créer une classe par défaut publique
    Par Momoth dans le forum Visual Studio
    Réponses: 2
    Dernier message: 13/10/2014, 19h39
  2. Instanciation par une classe unique
    Par fantomas75010 dans le forum Débuter avec Java
    Réponses: 7
    Dernier message: 04/03/2011, 10h27
  3. créer une classe avec nom demandé par std::cin
    Par Sba3Net dans le forum C++
    Réponses: 3
    Dernier message: 24/12/2008, 23h18
  4. Réponses: 10
    Dernier message: 26/06/2008, 11h25

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