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

SL & STL C++ Discussion :

Retourner un conteneur de pointeurs d'objets constants


Sujet :

SL & STL C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre chevronné
    Avatar de Florian Goo
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    680
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2008
    Messages : 680
    Par défaut Retourner un conteneur de pointeurs d'objets constants
    Bonjour à tous,

    J'ai un doute sur une implémentation. Quel est le meilleur moyen de retourner un conteneur de pointeurs d'objets de façon à ce que les objets ne puissent pas être accédés de façon non-const ?

    Soit une classe « panier » contenant une liste d'objets de type « pomme ».
    De façon naïve, on commence par écrire le code suivant :
    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
     
    #include <list>
     
    class pomme
    {
        public:
            void
            set_taille(int){/*...*/};
     
        //...
    };
     
    class panier
    {
        public:
            //...
     
            const std::list<pomme*>&
            get_pommes() const
            {
                return pommes_;
            }
     
        private:
            std::list<pomme*> pommes_;
    };
    Or, de cette façon, rien ne nous empêche d'appeler une méthode non-const d'un des objets pomme du conteneur (ce qui est tout à fait logique, mais totalement indésirable) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    void
    foo(const panier& un_panier)
    {
        const std::list<pomme*>& pommes = un_panier.get_pommes();
        if(!pommes.empty()) pommes.back()->set_taille(3);
    }

    Quelle est la bonne façon de faire dans un tel cas de figure ?
    Il y a trois méthodes qui me viennent à l'esprit :
    1. ne pas écrire d'accesseur direct à la liste, mais interfacer son accès via un itérateur qui renverra des const pomme* const ;
    2. gérer deux listes : une contenant des pomme* et une autre des const pomme* ;
    3. modifier l'accesseur à la liste de façon à ce qu'il retourne un objet proxy encapsulant la liste et proposant une interface empêchant une modification des objets pointés (probablement à base d'itérateur similaire à la première méthode).


    La méthode 1 induit l'écriture d'un tas de méthodes membres (get_pommes_begin_iterator(), _end_iterator(), sans parler d'empty() et de size()), ce qui serait extrêmement lourd).
    La méthode 2 me semble être la plus simple à mettre en place.
    La méthode 3 semble propre elle aussi (quoique je n'ai pas énormément creusé cette possibilité), mais si elle était généralement utilisée on aurait des classes proxy template déjà présentes dans la STL ou dans Boost, mais il n'y existe rien de tel à ma connaissance.

    Si je devais faire un choix moi-même, ce serait donc la méthode 2.
    Toutefois, j'aimerais beaucoup avoir vos différents avis. Utilisez-vous une autre méthode à laquelle je n'ai pas pensée ?

    J'attends vos réponses
    Cours : Initiation à CMake
    Projet : Scalpel, bibliothèque d'analyse de code source C++ (développement en cours)
    Ce message a été tapé avec un clavier en disposition bépo.

  2. #2
    Membre éclairé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Par défaut
    Ce n'est peut-être pas très élégant, mais faire un cast pour que get_pommes retourne const std::list<const pomme*>& ?

  3. #3
    Membre chevronné
    Avatar de Florian Goo
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    680
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2008
    Messages : 680
    Par défaut
    Eh non, c'est pas valide, comme cast
    Cours : Initiation à CMake
    Projet : Scalpel, bibliothèque d'analyse de code source C++ (développement en cours)
    Ce message a été tapé avec un clavier en disposition bépo.

  4. #4
    Membre expérimenté Avatar de Nogane
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    241
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 241
    Par défaut
    Bonsoir,
    Je me permet de rajouter une solution qui a une petite chance de convenir:
    Avec un "const boost::ptr_list" les éléments contenu sont constant eux aussi.

  5. #5
    Membre chevronné
    Avatar de Florian Goo
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    680
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2008
    Messages : 680
    Par défaut
    Dans mon cas particulier, mes objets sont alloués sur la pile, puis déplacés (move semantics de C++0x) dans un conteneur d'objets (et non de pointeurs d'objet). Le conteneur de pointeurs d'objet est là pour faire du polymorphisme.
    Par exemple, pour rester dans l'esprit de l'exemple que j'ai donné, ça donnerait plutôt ça :
    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
     
    class panier
    {
        public:
            void
            add(pomme&& p)
            {
                pommes_.push_back(p);
                pomme& p_ref = pommes_.back();
                fruits_.add(&p_ref);
            }
     
            void
            add(poire&& p)
            {
                poires_.push_back(p);
                poire& p_ref = poires.back();
                fruits_.add(&p_ref);
            }
     
            void
            add(scoubidoubidou&& s)
            {
                //...
            }
     
            const std::list<fruit*>
            get_fruits() const
            {
                return fruits_;
            }
     
        private:
            std::list<fruit*> fruits_;
            std::list<pomme> pommes_;
            std::list<poire> poires_;
            std::list<scoubidoubidou> scoubidoubidou_;
            //...
    };
    Quoi qu'il en soit, je ne pense pas qu'on puisse parler d'objets alloués sur le tas dans ce cas-là. Par conséquent, boost::ptr_list n'est pas envisageable.
    Cours : Initiation à CMake
    Projet : Scalpel, bibliothèque d'analyse de code source C++ (développement en cours)
    Ce message a été tapé avec un clavier en disposition bépo.

  6. #6
    Membre émérite

    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    717
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 717
    Par défaut
    Pour ma part j'utilise std::vector à 99.9% du temps, les 0.1% sont des std::set ou std::map (je crois que je n'ai jamais trouvé un cas concret où l'utilisation de std::list se justifiait) alors une interface d'accès à base d'index convient souvent :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class panier
    {
    public:
      int nombre_de_fruits() const {return int(fruits_.size());}
      fruit const& fruit(int i) const {return *fruits_[i];}
     
    private:
      std::vector<fruit*> fruits_;
    };

  7. #7
    Membre éclairé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Par défaut
    Citation Envoyé par Florian Goo Voir le message
    Eh non, c'est pas valide, comme cast
    En trichant un peu si.
    Je le reconnais, c'est un peu gonfflé, mais ça fonctionne très bien.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    std::vector<const unsigned *> & foo()
    {
    	std::vector<unsigned *> u_v;
     
    	void *vp=&u_v;
    	std::vector<const unsigned *> *cu_v_ptr=reinterpret_cast<std::vector<const unsigned *> *>(vp);
    	return *cu_v_ptr;
    }
     
    int main()
    {
    	std::vector<const unsigned *> & r=foo();
    }

  8. #8
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Par défaut
    Plutôt que de retourner un conteneur, tu pourrais pas plutôt fournir une paire d'itérateurs, ou une range ?
    Tu fais un simple adapteur autour de ton conteneur, ça fait deux lignes et c'est bon...

    return transformed(v, static_cast_<const unsigned*>(_1));

    la deuxième ligne étant un typedef du type de retour de cette expression.

  9. #9
    Membre chevronné
    Avatar de Florian Goo
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    680
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2008
    Messages : 680
    Par défaut
    Oui, c'est ce que j'ai tenté en premier avec ce type de code :
    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 panier
    {
        public:
            //...
     
            std::list<fruit*>::const_iterator
            get_fruits_begin() const
            {
                return fruits_.begin();
            }
     
            std::list<fruit*>::const_iterator
            get_fruits_end() const
            {
                return fruits_.end();
            }
     
        private:
            std::list<fruit*> fruits_;
            std::list<pomme> pommes_;
            std::list<poire> poires_;
            std::list<scoubidoubidou> scoubidoubidou_;
            //...
    };
    Mais après il aurait fallut écrire get_fruits_size(), get_fruits_empty(), etc… ce qui au final donne une trop grande quantité de code.

    Ceci dit, si tu as une solution élégante similaire requérant une quantité moindre de code, je suis partant . Retourner une paire d'itérateur semble être la solution privilégiée par les experts (par exemple, l'implémentation du pattern Composite du livre du GoF).

    Cette fonction transformed() provient-elle de boost ? Le grand fan de Boost que tu es me souffle que oui. Je vais me renseigner sur boost::iterator
    Cours : Initiation à CMake
    Projet : Scalpel, bibliothèque d'analyse de code source C++ (développement en cours)
    Ce message a été tapé avec un clavier en disposition bépo.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Pointeur constant sur objet constant
    Par escafr dans le forum C++
    Réponses: 4
    Dernier message: 30/06/2008, 17h23
  2. Tableau de pointeurs sur objets
    Par bassim dans le forum C++
    Réponses: 11
    Dernier message: 13/12/2005, 19h45
  3. [TTreeView] Problème avec les pointeurs d'objet
    Par BlackWood dans le forum Composants VCL
    Réponses: 2
    Dernier message: 02/07/2004, 14h31
  4. [Debutant VC++.net] Obtenir un pointeur sur objet
    Par SteelBox dans le forum MFC
    Réponses: 6
    Dernier message: 17/06/2004, 18h36
  5. Réponses: 6
    Dernier message: 03/09/2003, 10h29

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