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 :

Fonction qui renvoie un int (*)[15]


Sujet :

C++

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Ben, ca n'a rien de vraiment compliqué...

    C'est juste logique, et on va donc réexpliquer

    Quand tu déclare un tableau tab[linge][colone], c'est un tableau qui fait linge*colone éléments

    Quand tu passe à la première colone de la deuxième ligne, c'est, normalement que tu a déjà parcouru la première ligne, et tu te trouve donc au "colone"ieme+1 élément...

    Comme les index commencent à 0 en C et en C++, la première ligne et la première colone ont l'index 0, et donc le premier élément que l'on trouve à la ligne 1 (la deuxième) est en fait l'élément qui se trouve à l'index ... colone

    On *pourrait* envisager d'inverser la place de l'index des ligne et des colones dans le calcul, mais, l'idée de base est d'utiliser la même convention que ce qui se fait pour la gestion des matrices en mathématiques: M(ligne, colone)

    Si donc tu applique le calcul numéro_ligne * nombre_element_par_colone + numéro_colone, la priorité de la multiplication sur l'addition fait que le calcul tombe juste parce que cela revient à (calculer numéro_ligne* nombre_element_par_colone) + numéro_colone

    Tu vois, y a rien de tellement compliqué à tout cela
    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

  2. #22
    Expert confirmé

    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Février 2007
    Messages
    4 253
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Février 2007
    Messages : 4 253
    Billets dans le blog
    3
    Par défaut
    Je vais faire mon ch...eur

    Mais conceptuellement, y a un problême quand même....

    D'un coté tu mets le membre privé (donc le but est d'en cacher l'implémentation), et d'un autre côté tu "fixes" et publies l'implémentation en renvoyant un tableau...
    Si c'est pour faire ca... autant mettre le membre public ! Enfin bon....

    Sinon, peut être que ca te suffirai amplement (en tout cas, c'est ce que j'aurai fait):
    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
     
    class Carte
    {
         // implémentation privée...
         int   m_carte[1000][15];
         inline void check(int x, int y) {
              assert(x>=0 && x<1000);
              assert(y>=0 && y<15);
         }
     
     
         public:
     
              inline int   getAt(int x, int y) const {
                  check(x,y);
                  return m_carte[x][y];
              }
     
              inline void  setAt(int x, int y, int value) {
                   check(x,y);
                   m_carte[x][y] = value;
              }
    };

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Pas forcément...

    Avoir un "getter" et un "setter" permet, justement, d'avoir une abstraction supplémentaire...

    Un tableau à deux dimensions peut être représenté par plusieurs choses:

    • type tab[ligne*colone]
    • type mat[ligne][colone]
    • vector<type> tab(ligne*colone)
    • vector< vector<type> > mat
    • ...

    Tu peux, à n'importe quel moment, décider de modifier la manière dont les (ligne*colone) éléments sont représenté en mémoire, et ce pour des raisons qui ne tiennent peut être qu'à toi

    Si tu fournis le membre avec une visibilité publique, tu accepte implicitement que tout le monde utilise directement le membre en question...

    Si tu décide de modifier la manière dont le membre est représenté en mémoire, non seulement, tout ton code devra être revu pour l'adapter à la nouvelle représentation, mais, si ton code a pour but de fournir une bibliothèque, tout le code des utilisateurs de la bibliothèque devra être modifié... Avec les problèmes de compatibilité entre les versions que cela implique.

    Par contre, si tu fournis le membre avec une visibilité privée, et que tu fournis des méthodes qui vont bien (getters et setters, mais aussi, pourquoi pas, la possibilité de modifier les dimensions de ta matrice), dans l'ensemble du code qui sera présenté, il n'y aura qu'un nombre restreint d'endroits à modifier pour adapter à la nouvelle représentation en mémoire du membre... avec un avantage certain au niveau de la compatibilité...

    Cela n'a pas l'air énorme, mais n'avoir qu'à modifier deux ou trois fonctions (ou à peine plus si tu envisage des fonctions push_front/push_back, insert, remove, clear etc) plutôt que de devoir parcourir l'ensemble du code pour s'assurer que tu modifies tous les endroits où tu accède au membre public, ca facilite énormément la tache de mise à jour
    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. #24
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Sauf qu'en l'occurrence le "getter" possède un type de retour tel que changer l'implémentation n'est pas possible. Je souhaite bien du courage à qui veut implémenter par exemple un "getter" retournant par exemple un int** qui marche aussi bien si l'implémentation derrière est un int** et si c'est un vector<int>...

    Dans ce genre de cas, je préfère :
    - Soit fournir dans la classe des fonctions d'accès à une case du tableau uniquement
    - Soit ajouter un niveau d'abstraction supplémentaire, et avoir un type genre Matrice, l'utiliser dans l'implémentation comme l'interface de ma classe, et si je veux changer des choses, c'est ce type que je modifie.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Sauf qu'en l'occurrence le "getter" possède un type de retour tel que changer l'implémentation n'est pas possible. Je souhaite bien du courage à qui veut implémenter par exemple un "getter" retournant par exemple un int** qui marche aussi bien si l'implémentation derrière est un int** et si c'est un vector<int>...

    Dans ce genre de cas, je préfère :
    - Soit fournir dans la classe des fonctions d'accès à une case du tableau uniquement
    - Soit ajouter un niveau d'abstraction supplémentaire, et avoir un type genre Matrice, l'utiliser dans l'implémentation comme l'interface de ma classe, et si je veux changer des choses, c'est ce type que je modifie.
    De fait... j'aurais du préciser que cela impliquerait sans doute un niveau d'abstraction supplémentaire, mais il n'empêche que le fait d'avoir "getter", "setter" et une encapsulation de données correcte reste quand même de nature à améliorer la compatibilité entre les versions
    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. #26
    Expert confirmé

    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Février 2007
    Messages
    4 253
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Février 2007
    Messages : 4 253
    Billets dans le blog
    3
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Dans ce genre de cas, je préfère :
    - Soit fournir dans la classe des fonctions d'accès à une case du tableau uniquement
    - Soit ajouter un niveau d'abstraction supplémentaire, et avoir un type genre Matrice, l'utiliser dans l'implémentation comme l'interface de ma classe, et si je veux changer des choses, c'est ce type que je modifie.
    Exactement (voir d'ailleurs le code que je proposais)...

    Koala a du lire mon message en diagonale !
    Ce que je disais c'est "si c'est pour avoir un getter qui renvoit un tableau de int** alors ca veut dire que l'implémentation est fortement liée à un int** !" et dans l'exemple donné, autant passer le membre public... tout changement d'implémentation modifiant forcément le getter, et, par voie de conséquence, les utilisateurs de la classe.

    Des getter/setter sur une valeur permettent de faire abstraction de comment ces valeurs sont organisées en mémoire.

    De plus un getter qui renvoit un int** veut dire que derrière, c'est obligatoirement un int** qu'on a. Pourquoi ? parceque sinon, il devrait aussi y avoir un mécanisme pour indiquer à l'objet d'origine qu'on a modifié les valeurs dans le tableau !

    La fonction renverrait un const int** .... par contre....

  7. #27
    Membre Expert

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 294
    Détails du profil
    Informations personnelles :
    Localisation : Royaume-Uni

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 294
    Par défaut
    Salut,

    A mon avis un accesseur c'est bien souvent un code smell.
    Pas seulement parce que ça bousille l'encapsulation mais plutôt parce que ça inverse le sens du flux d'exécution et du coup ça inverse aussi les dépendances entre objets.

    Par exemple ici on obtient une dépendance depuis d'autres objets vers Carte, uniquement pour pouvoir faire un 'get' et donc pour obtenir des données.
    L'orienté objet est quand même plus axé traitements que données (c'est un des courants en tous cas), idéalement il faut s'efforcer de transformer ce genre de dépendance pour les inverser : que l'objet Carte avertisse les autres objets qui en ont besoin lors d'une modification (pattern observateur, etc..).

    Ca a l'air anecdotique et on se dit "holala la grosse artillerie qu'est l'observateur c'est quand même plus simple de faire un get", néanmoins plus on systématise cette inversion de contrôle et plus on voit les avantages se dégager dont la plus importante est je trouve la facilité de mise en place de proxies (vas-y faire un proxy sur un accesseur qui renvoit un int** pour décrire la carte).
    Ca permet aussi de "lisser" les dépendances (et le flux d'exécution) en les mettant toutes dans le même sens : on peut les suivre linéairement sans avoir besoin de faire des aller-retours tout le temps (typiquement ce que fait un accesseur).
    Pour donner une image ça transforme un diagramme de séquence très en hauteur en un tout en largeur.

    Ca demande de se forcer un peu au début, mais ça vaut franchement le coup. Au final je trouve ça beaucoup plus simple, plus modulaire, plus souple, etc..

    MAT.

  8. #28
    Membre éprouvé
    Lycéen
    Inscrit en
    Juillet 2007
    Messages
    148
    Détails du profil
    Informations personnelles :
    Âge : 34

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Juillet 2007
    Messages : 148
    Par défaut
    Citation Envoyé par Mat007 Voir le message
    Salut,

    A mon avis un accesseur c'est bien souvent un code smell.
    Pas seulement parce que ça bousille l'encapsulation mais plutôt parce que ça inverse le sens du flux d'exécution et du coup ça inverse aussi les dépendances entre objets.

    Par exemple ici on obtient une dépendance depuis d'autres objets vers Carte, uniquement pour pouvoir faire un 'get' et donc pour obtenir des données.
    L'orienté objet est quand même plus axé traitements que données (c'est un des courants en tous cas), idéalement il faut s'efforcer de transformer ce genre de dépendance pour les inverser : que l'objet Carte avertisse les autres objets qui en ont besoin lors d'une modification (pattern observateur, etc..).

    Ca a l'air anecdotique et on se dit "holala la grosse artillerie qu'est l'observateur c'est quand même plus simple de faire un get", néanmoins plus on systématise cette inversion de contrôle et plus on voit les avantages se dégager dont la plus importante est je trouve la facilité de mise en place de proxies (vas-y faire un proxy sur un accesseur qui renvoit un int** pour décrire la carte).
    Ca permet aussi de "lisser" les dépendances (et le flux d'exécution) en les mettant toutes dans le même sens : on peut les suivre linéairement sans avoir besoin de faire des aller-retours tout le temps (typiquement ce que fait un accesseur).
    Pour donner une image ça transforme un diagramme de séquence très en hauteur en un tout en largeur.

    Ca demande de se forcer un peu au début, mais ça vaut franchement le coup. Au final je trouve ça beaucoup plus simple, plus modulaire, plus souple, etc..

    MAT.
    Et comment on fait ça?

  9. #29
    Membre Expert

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 294
    Détails du profil
    Informations personnelles :
    Localisation : Royaume-Uni

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 294
    Par défaut
    En pratique ici, avec 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
    17
    18
    19
    20
    21
    22
    23
    class MapObserver
    {
    public:
      virtual void Notify( int** map ) = 0;
    };
     
    class Map
    {
    public:
      explicit Map( MapObserver& observer )
        : observer_( observer )
      {}
     
    private:
      void Update()
      {
        observer_.Notify( map_ );
      }
     
    private:
      MapObserver& observer_;
      int** map_;
    };
    Il manque sans doute des const mais ça donne l'idée.
    Ensuite c'est donc Map qui est chargé de prévenir l'observateur en cas de modification via la méthode helper Update.

    Les avantages :
    . lorsque la carte change (on change de niveau par exemple) c'est Map qui déclenche la mise à jour et non du code extérieur, on a donc une meilleure encapsulation et on ne risque pas d'oublier de faire ce qu'il faut où il faut
    . si on rajoute une nouvelle raison pour changer la carte (typiquement on peut changer de carte en cours de jeu et non plus uniquement à l'initialisation au début) on a très peu de modifications à apporter au reste du code : tout le mécanisme existe déjà
    . si on souhaite qu'en fonction des joueurs la carte ne soit pas la même (typiquement ce qu'on appelle un 'fog of war', c'est à dire que certaines parties sont cachées à certains joueurs) on peut facilement (et de manière transparente pour tout le reste du système) intercaler un objet (appelé proxy) entre Map et le(s) 'vrai(s)' observateur(s) qui va implémenter aussi MapObserver et qui va filtrer la carte au passage (on peut aussi y parvenir avec un accesseur mais c'est souvent moins facile à mettre en place)
    . on peut facilement transformer un appel Notify( liste< Element > ) en 'n' appels Notify( Element ), ça permet souvent de simplifier un tas de trucs en centralisant les boucles sur les éléments (faites une seule fois dans Update et non dans le Notify de chaque observateur), et ça encapsule les conteneurs utilisés
    . etc..

    Bizarrement l'observateur ne fait pas partie des patterns décrit dans la FAQ alors que c'est sans doute un des trois plus importants (avec le proxy et la factory), il y a une description sur wikipedia mais qui n'est pas forcément fantastique...

    MAT.

  10. #30
    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
    Tu peux retourner un int(*)[15] (c'est-à-dire un pointeur vers un tableau de 15 entiers), comme proposé par l'erreur de ton compilateur.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int (*Carte::getMap())[15]
    {
        return m_carte;
    }

Discussions similaires

  1. Fonction qui renvoie un tableau?
    Par elm dans le forum C
    Réponses: 23
    Dernier message: 21/12/2005, 12h44
  2. Réponses: 31
    Dernier message: 25/10/2005, 18h26
  3. Fonction qui renvoie erreur dans mon état
    Par Daniel MOREAU dans le forum Access
    Réponses: 1
    Dernier message: 20/10/2005, 12h40
  4. [bioinfo] fonction qui renvoie chaîne à autre fonction
    Par perlaud dans le forum Bioinformatique
    Réponses: 11
    Dernier message: 16/07/2004, 15h06
  5. [VB6] Comment faire une fonction qui renvoie 2 résultats
    Par tazarine dans le forum VB 6 et antérieur
    Réponses: 10
    Dernier message: 15/01/2004, 00h13

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