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 :

Accès aux éléments d'un vector


Sujet :

C++

  1. #1
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2013
    Messages
    13
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2013
    Messages : 13
    Points : 13
    Points
    13
    Par défaut Accès aux éléments d'un vector
    Bonjour,

    J'ai un problème de conception autour des conteneurs, par exemple avec un vector. Je ne comprends pas très bien le niveau de constance en retournant un const vector& par une méthode.
    Cela va m'interdire d'utiliser les méthodes d’insertion et de suppression. Qu'en est-il au niveau des éléments du vector ?
    Par exemple si mon vector contient des pointeurs, pourrais-je modifier les objets sur lesquels ils pointent ?

    Si non, comment faire pour parcourir un conteneur à travers une classe sans renvoyer le conteneur pour éviter une suppression ou insertion ? Dois-je redéfinir un const-iterator en méthode public de ma classe sur mon vector ? Qu'en est-il des performances ?

    Je fais peut-être fausse route mais j'ai du mal à saisir le schéma de conception dans ce cas-là:
    -pas d'insertion ni de suppression
    -possibilité de parcourir le conteneur
    -et de modifier les objets à travers les pointeurs(éléments du conteneur)

  2. #2
    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
    Bonsoir,

    vector est une classe. Comme toute classe, si tu as un Class& tu ne peux utiliser que les méthodes const de ta classe.
    Les accesseurs [], at(), front(), back() (je crois pas en oublier) existent en const et retournent un .. const T&, où T est le contenu de ton vector.

    Sur un const vector<T*> tu pourras donc accéder à un... const T*.
    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.

  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,

    A priori, il est déjà purement et simplement aberrant d'avoir un accesseur sur ton vecteur!!!

    Ta classe s'occupe de gérer en interne plusieurs éléments d'un type donné Très bien : en vertu de la loi de Déméter(*), tu n'as, en tant qu'utilisateur, absolument pas à t'inquiéter de la manière dont ces éléments sont maintenus en place, c'est aussi simple et brutal que cela .

    Citation Envoyé par la loi de Déméter(*)
    Si un objet de type A manipule en interne un objet de type B, l'utilisateur de A ne devrait pas avoir à connaître le type B pour manipuler le type A
    Je vais prendre un exemple qui n'a rien à voir avec une quelconque collection d'objet, mais il t'aidera à comprendre ce que je veux dire.

    Mettons que tu doive modéliser une voiture. Ta classe Voiture disposera forcément d'un membre de type Reservoir qui correspond au réservoir de la voiture.

    Si tu n'es pas mécanicien, en tant qu'utilisateur de la classe Voiture, tu n'as absolument pas besoin de connaitre le type Reservoir. Tout ce que tu sais du réservoir, c'est la voiture qui te le donne au travers d'éléments comme les différents cadrans / témoins / écrans (biffer les mentions inutiles) et de la trappe de carburant qui utiliseront certains services rendus par la classe Reservoir : La décision de faire le plein sera prise sur base des cadrans / témoins / écrans et le remplissage du réservoir lui même sera effectué au travers de la trappe à carburant. La voiture devient une "boite noire" qui contient, entre autres, un réservoir mais dont personne n'a besoin de s'inquiéter.

    Ici, le type B (ou le réservoir) est une collection d'objets, mais le principe reste de stricte application!

    Le fait que les différents éléments soient maintenus en place grâce à un std::vector est, typiquement, un "détail d'implémentation" dont l'utilisateur de la classe ne devrait pas avoir connaissance.

    Et, avec un peu de chance -- en fonction du type des éléments qui sont maintenus par ta classe dans ce std::vector -- l'utilisateur ne devrait même pas avoir connaissance du type des éléments qui sont maintenu de la sorte

    Tu veux avoir une approche orientée objet C'est tout à ton honneur, mais tu dois alors avoir un schéma de pensée qui est orienté vers les comportements que tu es en droit d'attendre de la part de tes classes et non un schéma de pensée qui serait orienté vers les données qui permettent à tes classes de fournir les comportements attendus.

    Il est impossible de dire exactement quels seront les services que tu es en droit d'attendre de la part de ta classe, car cela dépend essentiellement du contexte et de la responsabilité que tu lui donnes.

    Mais on peut envisager le fait que ta classe expose une fonction size() permettant de savoir combien d'élément elle a en mémoire et qui renvoie simplement la valeur de la fonction correspondante du vecteur,

    Tout comme on peut envisager le fait qu'elle expose une fonction prenant différents arguments afin de permettre la modification (ou l'interrogation) d'un élément particulier.

    Mais, quoi qu'il en soit, plus tu arriveras à penser en termes de comportements, de services rendus par ta classe, plus tu arriveras à faire en sorte que les services rendus par ta classe le soient au travers de l'instance de la classe que tu manipule, plus tu t'éviteras des soucis à l'utilisation et, surtout, à l'occasion de modifications (si tu te rends compte par exemple que l'utilisation d'une std::map ou d'un std::set est bien plus appropriée pour gérer les différents objets).
    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

  4. #4
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2013
    Messages
    13
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2013
    Messages : 13
    Points : 13
    Points
    13
    Par défaut
    Merci !

    Effectivement, vous m'avez bien éclairé. Je me suis éloigné du principe de services et je ne connaissais pas la loi de Déméter
    Un des comportements de ma classe A est de pouvoir parcourir ma collection d'objet B géré en interne.
    Je crois avoir vu quelque part que l'on pouvait définir la nouvelle boucle for du c++11 for(B b : A) dans la classe A, je vais essayer de faire cela. Sinon je définirait un iterator sur ma classe A !

    Je dois effectivement penser d'abord en terme d'abstraction, l'implémentation étant à part

  5. #5
    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
    Citation Envoyé par Kreatore Voir le message
    Merci !

    Effectivement, vous m'avez bien éclairé. Je me suis éloigné du principe de services et je ne connaissais pas la loi de Déméter
    Y a pas de quoi
    Un des comportements de ma classe A est de pouvoir parcourir ma collection d'objet B géré en interne.
    Alors, commence par déterminer la responsabilité de ta classe.

    Ensuite, intéresses-toi à la manière dont tu veux pouvoir manipuler les éléments que ta classe contient au travers de la classe elle-même.

    Ce n'est, en effet, pas parce qu'une classe dispose d'une collection d'objets qu'il faut forcément permettre à l'utilisateur de la classe en question d'accéder aux éléments qu'elle contient .

    Cela te permettra de savoir s'il est plus intéressant de considérer le parcours des différents éléments comme un service rendu par ta classe ou s'il faut au contraire s'attendre à ce qu'elle permette d'accéder aux différents éléments qu'elle contient(par exemple au travers d'une fonction renvoyant un itérateur sur le premier élément et d'une autre renvoyant un itérateur sur "ce qui suit" le dernier élément).


    Je crois avoir vu quelque part que l'on pouvait définir la nouvelle boucle for du c++11 for(B b : A) dans la classe A, je vais essayer de faire cela. Sinon je définirait un iterator sur ma classe A !
    En fait, on peut écrire une boucle sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    std::vector<UnType> tab;
    for(UnType /* const & */ element : tab){
        /* ce qu'il faut faire */
    }
    MAIS ce n'est en définitive qu'un "sucre syntaxique" qui correspond sommes toutes à une boucle "pour" tout à fait standard.

    Si tu as une classe proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class MyClass{
        public:
            void useAllElements();
        private:
            std::vector<int> tab;
    };
    tu peux parfaitement utiliser cette boucle dans la fonction useAllElement, sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void MyClass::useAllElement(){
        for (auto elem : tab){
     
        }
    }
    Par contre, comme MyClass::tab est dans l'accessibilité privée, tu ne peux bien évidemment pas accéder à son contenu d'une quelconque manière
    Je dois effectivement penser d'abord en terme d'abstraction, l'implémentation étant à part
    Surtout!!!

    Même si ce n'est pas le principe de base de la programmation orientée objet (car le principe de base est la subtituabilité des objets), l'abstraction est indispensable pour avoir un schéma de pensée correct, par lequel on réfléchit en termes de "comment j'utilise la classe" plutôt qu'en termes de données qu'elle manipule.

    Quand tu auras pris le pli de penser de la sorte, tu auras ensuite plus facile à aborder le pardigme générique, où le schéma de pensée devient "je ne sais pas ce que je manipule, mais je sais comment je le manipule"
    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

  6. #6
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2013
    Messages
    13
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2013
    Messages : 13
    Points : 13
    Points
    13
    Par défaut
    Ah oui en effet, merci pour ces précisions !

    Je veux donc que ma classe propose le parcourt des éléments qu'elle contient. Je vais donc définir un itérateur autre que celui utilisé par mon implémentation vector ou list par exemple. Je me demande néanmoins si c'est très judicieux ou si je peux me contenter d'ajouter une surcouche sur l'itérateur de list par exemple.
    C'est à dire une classe itérateur qui contient l'itérateur de list et qui se contente de laisser la responsabilité des différentes méthodes à celui-ci ?

  7. #7
    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
    Pas besoin de redéfinir un itérateur
    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
    class Foo {
    public:
    	typedef std::vector<int>::const_iterator const_iterator;
    	typedef std::vector<int>::iterator iterator;
     
    	iterator begin() { return vec.begin(); }
    	const_iterator begin() const { return vec.begin(); }
     
    	iterator end() { return vec.end(); }
    	const_iterator end() const { return vec.end(); }
     
    private:
     
    	std::vector<int> m_vec;
    };
    En utilisant la classe, on sait que les iterators sont de type Foo::iterator / Foo::const_iterator.

    Que ce soit un typedef sur des itérateurs de vecteur, de list, de set etc.. ça ne change pas grand chose (oui ils ne permettent pas tous des accès aléatoire...). M'enfin, si en l'utilisant tu utilise le type Foo::iterator et non pas std::vector<int>::iterator, alors en interne Foo peut remplacer son std::vector par une std::list sans casser le code existant utilisant la classe. (Toujours en mettant de coté la perte d'accès aléatoire).

    Si abstraction il doit y avoir au niveau des itérateurs, ça serait seulement pour supprimer l'accès aléatoire, et donc avoir une liberté totale de changement de conteneur sans que ce n'est d'impact sur le code client.
    Faire l'inverse : simuler un accès aléatoire lorsqu'il n'est pas fourni (cas d'une list par exemple) est une grave erreur : on encourage le client à utiliser du code lent (O(n) au lieu de O(1)) en lui cachant le coût.

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

Discussions similaires

  1. Accès aux éléments de différentes frames
    Par metalpetsFR dans le forum Balisage (X)HTML et validation W3C
    Réponses: 7
    Dernier message: 16/09/2009, 06h36
  2. Accès aux éléments d'une structure
    Par licorne dans le forum Pascal
    Réponses: 1
    Dernier message: 15/02/2007, 17h44
  3. accès aux éléments d'une enum
    Par aymeric__ dans le forum C++
    Réponses: 6
    Dernier message: 17/08/2006, 21h17
  4. Réponses: 8
    Dernier message: 05/07/2006, 13h35
  5. [Rico] Accès aux éléments de la page de manière bizarre
    Par dodik dans le forum Bibliothèques & Frameworks
    Réponses: 3
    Dernier message: 22/02/2006, 17h35

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