web-dev-qa-db-fra.com

Permettre à une classe "ami" d'accéder uniquement à certains membres privés

Supposons que j'ai trois classes C++ FooA, FooB et FooC.

FooA a une fonction membre nommée Hello, je veux appeler cette fonction dans la classe FooB, mais je ne veux pas que la classe FooC puisse l'appeler. La meilleure façon de comprendre cela est de déclarer FooB comme une classe d'amis de FooA. Mais tant que je fais cela, tous les membres privés et protégés de FooA seront exposés, ce qui est tout à fait inacceptable pour moi.

Donc, je veux savoir s'il existe un mécanisme en C++ (03 ou 11) meilleur que la classe friend qui peut résoudre ce dilemme.

Et je suppose que ce sera bien si la syntaxe suivante est possible:

class FooA
{
private friend class FooB:
    void Hello();
    void Hello2();
private:
    void Hello3();
    int m_iData;
};

class FooB
{
    void fun()
    {
        FooA objA;
        objA.Hello()  // right
        objA.Hello2() // right
        objA.Hello3() // compile error
        ojbA.m_iData = 0; // compile error
    }
};

class FooC
{
    void fun()
    {
        FooA objA;
        objA.Hello()  // compile error
        objA.Hello2() // compile error
        objA.Hello3() // compile error
        ojbA.m_iData = 0; // compile error
    }
};
34
Archer

Il n'y a rien pour faire d'une classe un ami d'une fonction spécifique, mais vous pouvez faire de FooB un ami d'une classe "clé" avec un constructeur privé, puis avoir FooA::Hello prend cette classe comme paramètre ignoré. FooC ne pourra pas fournir le paramètre et ne pourra donc pas appeler Hello:

Ce modèle de protection d'accès orienté clé est-il un idiome connu?

29
Steve Jessop

Je pense que vous pouvez utiliser avocat-client ici.

Dans votre cas, l'exemple devrait être comme ceci

class FooA
{
private:
    void Hello();
    void Hello2();
    void Hello3();
    int m_iData;

    friend class Client;
};

class Client
{
private:
   static void Hello(FooA& obj)
   {
      obj.Hello();
   }
   static void Hello2(FooA& obj)
   {
      obj.Hello2();
   }
   friend class FooB;
};

class FooB
{
    void fun()
    {
        FooA objA;
        Client::Hello(objA);  // right
        Client::Hello2(objA); // right
        //objA.Hello3() // compile error
        //ojbA.m_iData = 0; // compile error
    }
};

class FooC
{
    void fun()
    {
        /*FooA objA;
        objA.Hello()  // compile error
        objA.Hello2() // compile error
        objA.Hello3() // compile error
        ojbA.m_iData = 0; // compile error*/
    }
};
23
ForEveR

Non, et ce n'est pas vraiment une limitation. À mon avis, la limitation est que friend - une arme contondante pour pirater les défauts de conception - existe en premier lieu.

Votre classe FooA n'a rien à faire avec FooB et FooC et "lequel devrait pouvoir l'utiliser". Il devrait avoir une interface publique, et peu importe qui peut l'utiliser. C'est le point de l'interface! Les fonctions d'appel dans cette interface devraient toujours laisser le FooA dans un état agréable, sûr, heureux et cohérent.

Et si vous craignez que vous utilisiez accidentellement l'interface FooA depuis un endroit où vous ne le vouliez pas, eh bien, ne le faites tout simplement pas; C++ n'est pas un langage adapté à la protection contre ce type d'erreurs utilisateur. Votre couverture de test devrait suffire dans ce cas.

Strictement parlant, je suis sûr que vous pouvez obtenir la fonctionnalité que vous recherchez avec un "modèle de conception" horriblement compliqué mais, honnêtement, je ne m'embêterais pas.

Si c'est un problème pour la sémantique de la conception de votre programme, alors je suggère poliment que votre conception a un défaut.

La solution la plus sûre consiste à utiliser une autre classe comme "intermédiaire" pour vos deux classes, plutôt que d'en faire une friend. Une façon de procéder est suggérée dans la réponse de @ForEveR, mais vous pouvez également effectuer des recherches sur les classes proxy et les autres modèles de conception qui peuvent s'appliquer.

1
Jerome Baldridge

Vous pouvez exposer partiellement les interfaces d'une classe à un client spécifié en l'héritant d'une classe d'interface.

class FooA_for_FooB
{
public:
    virtual void Hello() = 0;
    virtual void Hello2() = 0;
};

class FooA : public FooA_for_FooB
{
private: /* make them private */
    void Hello() override;
    void Hello2() override;
private:
    void Hello3();
    int m_iData;
};

class FooB
{
    void fun()
    {
        FooA objA;
        FooA_for_FooB &r = objA;
        r.Hello()  // right
        r.Hello2() // right
        objA.Hello3() // compile error
        objA.m_iData = 0; // compile error
    }
};

class FooC
{
    void fun()
    {
        FooA objA;
        objA.Hello()  // compile error
        objA.Hello2() // compile error
        objA.Hello3() // compile error
        objA.m_iData = 0; // compile error
    }
};

Ici, le contrôle d'accès est amélioré par la classe de base FooA_for_FooB. Par une référence de type FooA_for_FooB, FooB peut accéder aux membres définis dans FooA_for_FooB. Cependant, FooC ne peut pas accéder à ces membres car ils ont été remplacés en tant que membres privés dans FooA. Votre objectif peut être atteint en n'utilisant pas le type FooA_for_FooB dans FooC, ou tout autre endroit sauf FooB, qui peut être conservé sans y prêter beaucoup d'attention.

Cette approche n'a pas besoin de friend, ce qui simplifie les choses.

Une opération similaire peut être effectuée en rendant tout privé dans une classe de base et en enveloppant et exposant de manière sélective certains des membres en tant que public dans la classe dérivée. Cependant, cette approche peut parfois nécessiter un laid abaissement. (Parce que la classe de base deviendra la "devise" de tout le programme.)

1
rolevax

Vous aurez besoin d'héritage. Essaye ça:

// _ClassA.h
class _ClassA
{
  friend class ClassA;
private:
  //all your private methods here, accessible only from ClassA and _ClassA.
}

// ClassA.h
class ClassA: _ClassA
{
  friend class ClassB;
private:
  //all_your_methods
}

De cette façon, vous avez: ClassB est le seul à pouvoir utiliser ClassA. ClassB ne peut pas accéder à _ClassA méthodes, privées.

0
user3737631

L'idée de friend est d'exposer votre classe à un ami.

Vous pouvez être plus précis sur ce que vous exposez de deux manières:

  1. Hérité de FooA, de cette façon, seules les méthodes protégées et publiques sont exposées.

  2. Ne liez d'amitié qu'avec une certaine méthode, de cette façon seule cette méthode aura accès:

.

 friend void FooB::fun();
0
Yochai Timmer