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

  1. #1
    Membre du Club
    Inscrit en
    mars 2007
    Messages
    119
    Détails du profil
    Informations forums :
    Inscription : mars 2007
    Messages : 119
    Points : 53
    Points
    53

    Par défaut Visibilité d'une fonction dans une classe appelée par une autre classe

    Bonjour, j'aimerais améliorer la lisibilité d'un de mes codes par d'autres développeurs et je me pose une question sur la visibilité d'une fonction.

    Voici un code simplifié :

    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
    class A {
    	A() {}
    	B & getB(unsigned int index) {return B[index];}
    	void update() {
    		// update internal value
    		for(B & element:m_data)
    			element.updateParam3()*;
    	}
    private:
    	vector<B> m_data;
    }
     
    class B {
    	B() {}
    	updateParam1() { // some code }
    	updateParam2() { // some code }
    	updateParam3() { // some code }
    }
    Je peux accéder aux éléments B qui sont dans A::m_data avec A::getB(index).updateParamX() pour les paramètres 1 et 2. C'est bien le comportement que j'attends.

    Pour B::updataParam3(), j'aimerais interdire l'accès en passant directement par A::getB(index).updateParam3() car je veux garder un état interne dans A suite à la modification de mon paramètre 3. Donc j'aimerais forcer tout développer à appeler A::update() à la place. En d'autres termes, j'aimerais que B::updateParam3() soit privé sauf quand je l'appelle depuis la classe A. Est-ce que ceci est possible ?

    Merci d'avance !

  2. #2
    Membre habitué
    Homme Profil pro
    Développeur
    Inscrit en
    août 2011
    Messages
    53
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : août 2011
    Messages : 53
    Points : 137
    Points
    137

    Par défaut

    Oui avec l'amitié, regarde le mot clé friend.

  3. #3
    Membre du Club
    Inscrit en
    mars 2007
    Messages
    119
    Détails du profil
    Informations forums :
    Inscription : mars 2007
    Messages : 119
    Points : 53
    Points
    53

    Par défaut

    Merci, je découvre ce mot-clé ! J'ai essayé le code suivant mais j'ai une erreur de compil :

    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 A {
    public:
    	A() {}
            friend void B::updateParam3();
    	B & getB(unsigned int index) {return B[index];}
    	void update() {
    		// update internal value
    		for(B & element:m_data)
    			element.updateParam3()*;
    	}
    private:
    	vector<B> m_data;
    }
     
    class B {
    public:
    	B() {}
    	void updateParam1() { // some code }
    	void updateParam2() { // some code }
    private:
    	void updateParam3() { // some code }
    }
    La fonction B::updateParam3() n'est donc plus visible mais le compilateur me dit qu'elle est privée donc non accessible. Je n'ai pas du tout comprendre... Est-ce possible de m'aiguiller un peu plus ? Merci !

  4. #4
    Membre habitué
    Homme Profil pro
    Développeur
    Inscrit en
    août 2011
    Messages
    53
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : août 2011
    Messages : 53
    Points : 137
    Points
    137

    Par défaut

    Il y a peut être un problème si A ne connait pas intégralement B. Que te sors le compilateur ?

  5. #5
    Membre habitué
    Homme Profil pro
    Développeur
    Inscrit en
    août 2011
    Messages
    53
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : août 2011
    Messages : 53
    Points : 137
    Points
    137

    Par défaut

    A non pardon en fait tu dois déclarer dans B que A::update() est ton amie pas l'inverse.

  6. #6
    Membre du Club
    Inscrit en
    mars 2007
    Messages
    119
    Détails du profil
    Informations forums :
    Inscription : mars 2007
    Messages : 119
    Points : 53
    Points
    53

    Par défaut

    Merci, ça progresse ! Quand je fais ça, ça marche :

    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 A {
    public:
    	A() {}
    	B & getB(unsigned int index) {return B[index];}
    	void update() {
    		// update internal value
    		for(B & element:m_data)
    			element.updateParam3()*;
    	}
    private:
    	vector<B> m_data;
    }
     
    class A;
    class B {
    public:
            friend class A;
    	B() {}
    	void updateParam1() { // some code }
    	void updateParam2() { // some code }
    private:
    	void updateParam3() { // some code }
    }
    J'ai essayé de réduire un peu l'exposition (une fonction plutôt que la classe) mais dans mon projet, j'ai 2 headers séparés. Et je tombe sur un problème sur les include à mon avis ("unterminated conditional directive"). Comment m'en sortir si les 2 classes doivent se connaître mutuellement ?

    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
    #ifndef A
    #define A
    #include "B.h" -> nécessaire pour m_data
    
    class A {
    public:
    	A() {}
    	B & getB(unsigned int index) {return B[index];}
    	void update() {
    		// update internal value
    		for(B & element:m_data)
    			element.updateParam3()*;
    	}
    private:
    	vector<B> m_data;
    }
    #endif
    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
     
    #ifndef B
    #define B
    #include "A.h" -> nécessaire pour le friend
    
    class A;
    class B {
    public:
            friend void A::update();
    	B() {}
    	void updateParam1() { // some code }
    	void updateParam2() { // some code }
    private:
    	void updateParam3() { // some code }
    }
    
    #endif [UPDATE/EDIT]

  7. #7
    Membre habitué
    Homme Profil pro
    Développeur
    Inscrit en
    août 2011
    Messages
    53
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : août 2011
    Messages : 53
    Points : 137
    Points
    137

    Par défaut

    Il manque pas un #endif ?

  8. #8
    Membre du Club
    Inscrit en
    mars 2007
    Messages
    119
    Détails du profil
    Informations forums :
    Inscription : mars 2007
    Messages : 119
    Points : 53
    Points
    53

    Par défaut

    En effet, dans mon exemple ici il manque le #endif. Par contre, je le compile dans mon code "complet" où il ne manque pas le #endif et j'ai le pb de compil.

  9. #9
    Rédacteur/Modérateur

    Homme Profil pro
    Network game programmer
    Inscrit en
    juin 2010
    Messages
    5 861
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : juin 2010
    Messages : 5 861
    Points : 25 893
    Points
    25 893

    Par défaut

    Ton truc est impossible (et donc sûrement une idée bancale).

    Pour utiliser vector<B> il faut connaître B.
    Pour faire friend A::Toto(); il faut connaître A.

    Tu as 2 solutions
    - soit tu utilises un vector<B*> qui ne nécessite plus qu'une forward declaration de B
    - soit tu utilises friend A; qui nécessite qu'une forward declaration de A
    Et dans tous les cas, l'implémentation se fait bien entendu dans son propre fichier source et non dans le header.
    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.

  10. #10
    Expert éminent
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    juillet 2013
    Messages
    2 822
    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 : 2 822
    Points : 6 234
    Points
    6 234

    Par défaut

    Il faut connaitre le contexte du pourquoi du comment et de plus passer par un accesseur pour juste appeler une méthode c'est un truc à ne pas faire

    Et pourquoi pas un code ultra simple (si le nombre de paramètres reste petit)


    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
    40
    41
    42
    43
    44
    45
    class A {
    public:
     
    	A() {}
     
    	void updateParam1() {
    		for(B& element:m_data) {
    			 // some code;
    		}
            }
     
     
    	void updateParam2() {
    		for(B& element:m_data) {
    			 // some code ;
    		}
            }
     
     
    	void update() {
    		// update internal value
     
    		for(B& element:m_data)
    			updateParam3(element);
            }
     
     
    private: // either private or public or external
     
    	struct B { // dto - data transfer object  : only data no methods
    	public:
     
    		B() {}
    	}
     
     
    private:
     
    	updateParam3(/*const*/ A::B&) { // some code }
     
     
    private:
     
    	vector<A::B> m_data;
    }

  11. #11
    Membre du Club
    Inscrit en
    mars 2007
    Messages
    119
    Détails du profil
    Informations forums :
    Inscription : mars 2007
    Messages : 119
    Points : 53
    Points
    53

    Par défaut

    Merci pour vos messages, j'ai compris pourquoi ça ne marchait que dans le cas d'un friend classe !

    @foetus : le nombre de paramètre n'est pas petit et je n'appelle pas systématiquement les mises à jour de paramètres sur tous les éléments A::m_data. La structure B est une classe assez conséquente avec des méthodes propres. Je n'ai pas compris ce qui est à ne pas faire, et je veux bien comprendre pourquoi l'approche n'est pas la bonne pour pouvoir l'améliorer ! Merci d'avance !

  12. #12
    Expert éminent
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    juillet 2013
    Messages
    2 822
    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 : 2 822
    Points : 6 234
    Points
    6 234

    Par défaut

    Citation Envoyé par julieng31 Voir le message
    Je n'ai pas compris ce qui est à ne pas faire, et je veux bien comprendre pourquoi l'approche n'est pas la bonne pour pouvoir l'améliorer ! Merci d'avance !
    Il faut éviter de manipuler B au travers de A

    Loi de Déméter (<- lien wiki en français)

  13. #13
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    Consultant informatique
    Inscrit en
    octobre 2004
    Messages
    11 085
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Belgique

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : octobre 2004
    Messages : 11 085
    Points : 27 205
    Points
    27 205

    Par défaut

    Salut,

    J'ai juste une question "idiote" pour toi :

    Pourquoi voudrais tu interdire l'accès à une fonction -- quelle qu'elle soit -- (on parle de updateParam3, dans le cas présent), alors que, de toutes évidences, cette fonction fait partie ... des services auxquels tu t'attends à pouvoir disposer au travers de ta classe

    De mon point de vue, cela n'a absolument aucun sens, et je m'explique:

    Tu dois considérer chaque classe comme un "fournisseur de services", de telle manière à ce qu'elle expose un minimum de comportements susceptibles de te permettre de manipuler ta classe de manière cohérente et "sécurisante".

    Or, si quelle que soit la raison, tu te retrouves à devoir faire appel à une fonction particulière de ta classe au travers d'un objet de type différent, c'est -- très clairement -- que cette fonction fait bel et bien partie ... des services que tu es en droit d'attendre de la part de la part de ta classe

    En outre, je subodore vaguement que la classe que tu as appelée B, avec ses fonctions updateParamXXX devrait être en réalité une classe (dont un nom explicite adapté serait peut être Parameter ) exposant une seule et unique fonction virtuelle (pure) update, qui pourrait servir de classe de base dans une hiérarchie de classes.

    Cela aurait pour principale conséquence le fait que tu devrais faire en sorte de stocker un tableau de pointeurs (de préférences intelligents) sur des objets "passant pour être du type" Parameter (et dont le type réel correspondrait aux différentes classes qui dérivent de ce type), mais qui te permettrait de parcourir l'ensemble des éléments sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    for(auto & elem : m_datas){
        elem.get()->update();
    }
    le comportement de update propre à chaque classe dérivée étant, bien entendu, redéfini de manière à s'adapter au type spécifique de chaque classe

    Mais, bien sur, il se peut aussi très bien que je me fourvoie sur ce coup, étant donné que l'on n'a absolument aucune idée de l'objectif que tu poursuis avec tes deux classes A et B, ou, du moins, qu'elles ne fournissent aucun indice quant à l'usage auquel tu les destines
    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

  14. #14
    Membre du Club
    Inscrit en
    mars 2007
    Messages
    119
    Détails du profil
    Informations forums :
    Inscription : mars 2007
    Messages : 119
    Points : 53
    Points
    53

    Par défaut

    Merci, je comprends mieux pourquoi mon approche est bancale.

    En en discutant, on m'a proposé de faire une classe "publique" PU qui hérite d'une classe "privée" PR. Ainsi, m_data serait un vecteur de PR. Les fonctions que je veux "cacher" seraient dans la classe PR quand les fonctions accessibles par getB() seraient dans la classe PU. La fonction getB() renvoyant une référence de type PU.

    Ca ressemblerait à ç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
    A {
    private:
        vector<PR> m_data;
        void update() {
            for(auto & elem:m_data)
                elem.updateParam3()
        }
        PU & getB(index) {return m_data[index]}
    }
    
    class PU {
    public:
        // Diverses fonctions accessibles par tous
    }
    
    class PR : public PU {
    public:
        PR() : PU() {}
        void updateParam3()
    }

  15. #15
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    Consultant informatique
    Inscrit en
    octobre 2004
    Messages
    11 085
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Belgique

    Informations professionnelles :
    Activité : Consultant informatique

    Informations forums :
    Inscription : octobre 2004
    Messages : 11 085
    Points : 27 205
    Points
    27 205

    Par défaut

    Juste un petit conseil : n'hésites pas à donner des noms cohérents à tes classes, à tes fonctions et à tes données, car, comme disait l'autre
    Citation Envoyé par Eragon (2006)
    Blissingr signifie le feu.

    C'est le feu.

    Connait le mot, et tu maîtrise la chose
    Dis toi que l'on passe beaucoup plus de temps à lire le code qu'à l'écrire. Même lorsque c'est du code que l'on a nous même écrit. Et que le temps passe: si tu dois relire ton code dans trois mois, six mois ou deux ans, le code qui te semble "clair comme de l'eau de roche" aujourd'hui te paraîtra tout de suite beaucoup moins clair "plus tard".

    Nous ne sommes plus à une époque à laquelle le moindre byte de stockage coûtait la moitié d'un bras. Profitons en donc pour donner des noms qui auront "quelques chances" de résister à l'assaut du temps: PU, PR, cela a sûrement une signification très claire et très précise pour toi (ou pour celui qui t'a conseillé ces noms), et c'est très bien. Mais, pour moi, qui n'était pas derrière ton épaule quand tu as écrit le code, qui ne sais même pas exactement ce que tu essayes de faire, cela ne veut absolument rien dire.

    Ajoute donc "suffisamment de lettres" à ces deux noms pour en faire des mots complets (voire des mots composés) qui veulent clairement dire quelque chose sans avoir recours à des abréviations... Tu t'en seras reconnaissant plus tard
    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

  16. #16
    Membre du Club
    Inscrit en
    mars 2007
    Messages
    119
    Détails du profil
    Informations forums :
    Inscription : mars 2007
    Messages : 119
    Points : 53
    Points
    53

    Par défaut

    Merci à tous pour vos réponses et conseils !

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

Discussions similaires

  1. Rangement de mes fonctions dans ma classe
    Par nonozor dans le forum Débuter
    Réponses: 1
    Dernier message: 11/06/2009, 12h42
  2. Réponses: 0
    Dernier message: 16/01/2009, 10h33
  3. Réponses: 11
    Dernier message: 14/01/2009, 12h03
  4. Réponses: 2
    Dernier message: 16/01/2008, 08h36
  5. appel à une fonction dans la classe Action
    Par imane_bennouna dans le forum Struts 1
    Réponses: 3
    Dernier message: 07/08/2006, 11h09

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