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 :

Typer un objet après sa création .?.


Sujet :

C++

  1. #1
    Membre régulier
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    54
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 54
    Points : 77
    Points
    77
    Par défaut Typer un objet après sa création .?.
    Bonjour à tous et merci par avance de vous intéresser à mon problème

    Dans mon optique de lire des fichiers définissant des objets se référençant entre eux, il m'arrive fréquemment d'avoir à créer un objet avant de connaître effectivement son type. Celui-ci ne sera connu que plus tard, lors de la définition de l'objet.

    Je m'explique.
    Admettons que j'aie l'architecture objet suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Base{ //definition }
     
    class A : public Base{ //definition }
     
    class C{
    public:
       Base* a;
       int attr;
    }
    Je lis ensuite des fichiers XML "peuplant" ce modèle de données et ayant la forme :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    <C id="ID_C"> <!-- instance d'objet de classe "C" et d'id "ID_C" -->
       <C.attr>4</C.attr>
       <C.a ref_id="ID_A"> <!-- l'objet référencé n'existe pas encore ! -->
    </C>
     
    <A id="ID_A"> <!-- instance d'objet de classe "A" et d'id "ID_A" -->
    </A>
    Bien sûr, il pourrait exister n'importe quel nombre de référence vers l'instance d'id "ID_A".

    Le problème est relativement simple. Il existe deux cas :
    - Contrairement à mon exemple, un objet "A" peut avoir été instancié avant d'être référencé par une autre instance "C", auquel cas il n'y a pas de difficulté puisqu'il me suffit de récupérer l'instance créée "A" et de la lier avec l'instance "C" :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    A* a= getAById("ID_A");
    C.a = a;
    - Ou bien comme dans mon exemple, un objet peut être référencé avant d'être instancié. Si "C" avait eu un attribut de type "A" et non de type "Base", il n'y aurait pas eu de problème puisque j'aurais pu créer un objet de type "A" d'id "ID_A" vide à la lecture de la définition de l'objet de type "C" d'id "ID_C" puis le setter correctement à la lecture de sa définition :
    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
     
    // A la lecture de "C" :
     
    // L'objet de type "A" d'id "ID_A" a-t-il déjà été référencé ?
    A* a = getAById("ID_A");
    if(a == NULL){
       a = new A;
       addIdA("ID_A", a);
    }
    C.a = a;
     
     
    // A la lecture de "A" :
    A* a = getAById("ID_A");
     
    // setter "a" correctement, tous les objets ayant une référence vers "a" auront ainsi la bonne version.
    Dans le cas qui m'intéresse, l'objet référencé est en fait un objet dérivé, que je ne peux donc pas créer s'il n'existe pas encore pour le setter à postériori puisque je ne sais pas quelle classe dérivée sera choisie. Pour reprendre l'exemple précédent, ce code est clairement faux :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    // A la lecture de "C" :
     
    // L'objet de type "Base" d'id "ID_A" a-t-il déjà été référencé ?
    Base* base = getBaseById("ID_A");
    if(base == NULL){
       base = new Base;
       addIdBase("ID_A", base);
    }
    C.a = base;
     
     
    // A la lecture de "A" :
    A* a = getAById("ID_A"); //en admettant que cette fonction me renvoie effectivement le bon objet, mais forcément de type réel "Base"
    Et là ça pète forcément.
    Comment faire pour que "C" garde sa référence vers "a" qu'il considère comme un objet de type "Base" mais qui est en fait de type "A" afin de le setter correctement à posteriori ? Le but étant bien sûr de ne faire qu'une passe et faire un minimum de calcul (c'est-à-dire ne pas reparcourir tous les objets référençant l'objet d'id "ID_A" pour mettre à jour la référence). Je ne sais pas trop comment faire pour indiquer dès la rencontre de "C" l'endroit où sera ensuite défini l'objet de type "Base" et de type réel "A" ou autre ...

    Merci beaucoup pour votre aide.

  2. #2
    Membre averti Avatar de Nogane
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    241
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 241
    Points : 323
    Points
    323
    Par défaut
    Bonsoirs,
    En effet changer le type d'un objet en cours de route est impossible, et si tu le réalloue, son adresse changera, et donc si tu référence les objet par leur adresse, c'est fichu.

    Si j'ai bien compris le problème...
    Je vois deux solutions:
    - Référencer les objets par un identifiant unique autre que leurs pointeur, et les ranger dans un tableau associatif permettant de les retrouver. Par exemple l'identifiant unique peut être crée par auto-incrément, ce qui fait que tu peut stoker tes objets dans un vector, et donc les retrouver assez rapidement.

    - Quand tu parse tu te contente de créer les objets ainsi qu'une liste des références. Et tu n'assigne les référence a tes objets qu'après.

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

    Pourquoi ne pas rendre, tout simplement, la classe qui se charge de la lecture du fichier amie de la classe de base et mettre la référence vers l'objet directement dans celle-ci

    Ta classe de base se présenterait alors 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
    class Base
    {
        public:
            Base(std::string const& id_):ref_(0),id_(id_){}
            virtual ~Base();
            std::string const& id() const{return id_;}
        private:
            Base* ref_;
            std::string id_;
            friend class Reader;
    };
    Et la classe de lecture disposerait d'un tableau assciatif dont la clé serait... l'id de l'objet et la valeur... un pointeur (de type Base) vers l'objet existant.

    Après avoir créé l'objet (dont tu sais le type), tu vérifie si l'identifiant de la référence existe, et si oui, tu la défini.

    La classe de lecture 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
    class Reader
    {
            typedef std::map<std::string,Base*>::const_iterator const_iterator;
        public:
           Reader(){}
           ~Reader()
           {
               /*libérons correctement la mémoire */
               for(const_iterator it=map.begin();it!=map.end();++it)
                   delete (*it).second;
           } 
           void read(/* parametre éventuels */)
           {
                /* je passe sur la lecture */
                Base* toadd;
                /* je passe sur la sélection et la création de l'objet à créer */
                const_iterator it=map_.find(id_reference_recuperee);
                if(it!=map_.end())
                   toadd->ref_ = (*it).second;
               map_.insert(std::make_pair(toadd->id(),toadd));
           }
        private:
            std::map<std::string, Base*> map_;
    };
    [EDTI] Je me rend compte que je ne donne que la moitié de la solution

    Ici, nous pourrons définir la référence que si elle a été créé avant l'objet en cours de création...

    Modifions donc un tout petit peu la classe de base en:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Base
    {
        public:
            Base(std::string const& id_):ref_(0),id_(id_), refid_(""){}
            virtual ~Base();
            std::string const& id() const{return id_;}
        private:
            Base* ref_;
            std::string id_;
            std::string refid_;
            friend class Reader;
    };
    et la fonction de lecture en
    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
           void read(/* parametre éventuels */)
           {
                /* je passe sur la lecture */
                Base* toadd;
                /* je passe sur la sélection et la création de l'objet à créer */
                const_iterator it=map_.find(id_reference_recuperee);
                if(it!=map_.end())
                   toadd->ref_ = (*it).second;
                else
                   toadd->refid_=id_reference_recuperee;
               map_.insert(std::make_pair(toadd->id(),toadd));
     
               /* une fois tous le fichier lu, définissions les références 
                * manquantes
                *
                * NOTA: cette partie pourrait très bien etre factorisée
                * dans une fonction séprée (updateReferences() ?? )
                */
              for(std::map<std::string, Base*>::iterator it=map_.begin();
                  it!=map.end();++it)
             {
                 if((*it).second->refid_!="")
                 {
                      const_iterator search=map_.find((*it).second->refid_);
                      if(search==map_.end())
                          throw ReferenceManquante();
                      (*it).second->ref_=(*search).second;
                      (*it).second->refid_="";
                 }
             }
           }
    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. [2008] Actualiser Management Studio après la création d'objet (table, vue etc)
    Par ulmeen dans le forum Développement
    Réponses: 2
    Dernier message: 28/03/2014, 15h47
  2. Réponses: 3
    Dernier message: 25/02/2010, 13h47
  3. [eZ Publish] Envoyer trois emails différents après la création d'un objet
    Par Locace dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 1
    Dernier message: 27/08/2008, 14h38
  4. Réponses: 17
    Dernier message: 03/05/2006, 15h31
  5. Réponses: 2
    Dernier message: 10/02/2006, 14h46

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