59 votes

nettoyer un équivalent ami granulaire C ++? (Réponse: idiome avocat-client)

Pourquoi est-ce que C++ ont public membres que n'importe qui peut l'appeler et friend des déclarations qui exposent tous private membres de donnée étrangères des classes ou des méthodes, mais n'offrent aucune syntaxe pour exposer membres en particulier étant donné que les appelants?

Je tiens à exprimer interfaces avec quelques routines être invoquée que par les appelants, sans avoir à donner aux appelants de l'accès complet à tous les soldats, qui se sent comme une chose raisonnable de vouloir. Le mieux que je pouvais venir avec moi-même (ci-dessous) et des suggestions faites par d'autres jusqu'à présent tournent autour des idiomes/motif de différentes indirectness, où vraiment, je veux juste un moyen d'avoir unique, simple définitions de classes d'indiquer explicitement que les appelants (plus granulaire que moi, mes enfants, ou absolument tout le monde) peut accéder à laquelle les membres. Quelle est la meilleure façon d'exprimer le concept ci-dessous?

// Can I grant Y::usesX(...) selective X::restricted(...) access more cleanly?
void Y::usesX(int n, X *x, int m) {
  X::AttorneyY::restricted(*x, n);
}

struct X {
  class AttorneyY;          // Proxies restricted state to part or all of Y.
private:
  void restricted(int);     // Something preferably selectively available.
  friend class AttorneyY;   // Give trusted member class private access.
  int personal_;            // Truly private state ...
};

// Single abstract permission.  Can add more friends or forwards.
class X::AttorneyY {
  friend void Y::usesX(int, X *, int);
  inline static void restricted(X &x, int n) { x.restricted(n); }
};

Je suis loin d'être un logiciel organisation de gourou, mais il se sent comme la simplicité de l'interface et le principe de moindre privilège vont directement à l'encontre de cet aspect de la langue. Une meilleure exemple pour mon désir peut être un Person classe déclarée méthodes comme takePill(Medicine *) tellTheTruth() et forfeitDollars(unsigned int) que seulement Physician, Judgeou TaxMan instances/méthodes membres, respectivement, devraient même songer à invoquer. Ayant besoin d'un temps de proxy ou de classes d'interface pour chaque aspect de l'interface se trouve mal avec moi, mais merci de le dire si vous le savez je suis en manque de quelque chose.

Réponse acceptée à partir de Drew Hall: Dr Dobbs, l'Amitié et le Procureur-Client Idiome

Le code ci-dessus à l'origine appelé la classe wrapper "Proxy" au lieu de "Procureur" et utilise des pointeurs au lieu de références, mais autrement a été équivalent à ce qui a Attiré trouvé, que j'ai ensuite considéré comme le meilleur en général de solution connue. (Ne pas tapoter sur le dos trop dur...) j'ai aussi changé la signature de "limité" pour démontrer paramètre de transfert. Le coût global de cet idiome est une classe et un ami de la déclaration par l'ensemble d'autorisations, un ami de la déclaration par ensemble approuvé de l'appelant, et un transfert wrapper par méthode exposée par le jeu d'autorisations. La plupart des meilleurs de la discussion ci-dessous tourne autour du renvoi d'appel standard qui est très semblable 'Clé' idiome évite au détriment de moins de protection directe.

84voto

Matthieu M. Points 101624

J'ai été en utilisant un modèle simple pour un certain temps maintenant. Vous ne savez pas si il a un nom ou quoi que ce soit, mais c'est exactement le problème que vous avez décrit, je suis enfin venu avec elle, après la lecture pour un certain temps à ce sujet sur le net.

L'idée est très simple, chaque classe déclare une Clé:

// foo.h
class Foo;

class FooKey { friend class Foo; FooKey() {} };

Toute classe qui souhaitent accorder particulières d'accès à l' Foo des cas, suffit de déclarer une méthode nécessitant un FooKey objet comme paramètre.

// bar.h
class FooKey;

class Bar
{
public:
  void mySpecialMethod(int a, FooKey const&);
};

Il n'est pas parfait, bien sûr, depuis lors, mySpecialMethod a FooKey objet et peut donc appeler n'importe quelle méthode qui en font la demande. Mais nous sommes en C++, donc on peut s'efforcer de les protéger contre Murphy, mais ne peut pas faire grand chose contre Machiavel.

Il est très facile, et une fois que vous prenez l'habitude de créer une touche de classe à chaque fois que vous créez une classe, alors il est facile d'ajouter une restriction de la méthode tout moment vous le souhaitez :)

EDIT:

En tenant compte des commentaires, j'ai pensé à la transmission question.

Il semble que l'utilisation de passage par valeur et de la prévention de la copie serait effectivement twart toute tentative de détournement de la clé:

class Foo;

class FooKey: boost::noncopyable { friend class Foo; FooKey() {} };

Utilisation:

void method(FooKey);

void Foo::test() { method(FooKey()); }

Pas de redirection:

void super(FooKey a) { method(a); } // compiler complains as FooKey not copyable

Et nous pouvons nous attendre à le compilateur pour appliquer des optimisations depuis FooKey est une classe vide. En fait, je semble recally qu'un gdb trace de la pile à ne pas montrer le paramètre du tout, mais j'ai peut-être tort.

EDIT 2: Luc Touraille noté après près de 2 ans (la dernière modification a eu lieu en juillet '10 !) le ci-dessus est en fait... de poisson, et plus probablement carrément erronée car FooKey ne peut pas générer un constructeur de copie à tous.

Version fixe (testé sur ideone):

class FooKey { friend class Foo; FooKey() {} FooKey(FooKey const&) {} };

Et en bonus, se débarrasser de la dépendance sur un coup de pouce ;)

EDIT 3: je viens de découvrir C++11 a amélioré cette fonctionnalité grâce à K-ballo, maintenant, nous pouvons industrialiser la production de la clé:

template <typename T>
class Key { friend class T; Key() = default; Key(Key const&) = default; };

Et avec ça:

class Bar { public: void special(int a, Key<Foo>); };

21voto

Drew Hall Points 15917

L' idiome avocat-client peut être ce que vous recherchez. Les mécanismes ne sont pas trop différents de votre solution de classe proxy membre, mais cette méthode est plus idiomatique.

3voto

Haspemulator Points 2945

Vous pouvez utiliser un modèle décrit dans Jeff Aldger du livre "C++ pour les programmeurs'. Il n'a pas de nom spécial, mais là, il est mentionné que "les pierres précieuses et les facettes'. L'idée de base est la suivante: au sein de votre classe principale qui contient toute la logique, vous définissez plusieurs interfaces (pas de véritables interfaces, tout comme eux) qui met en œuvre des sous-parties de cette logique. Chacun de ces interface (facette en termes de livre) qui donne accès à certains de la logique de la classe principale (pierre). Aussi, chaque facette détient le pointeur de pierre gemme de l'instance.

Qu'est-ce que cela signifie pour vous?

  1. Vous pouvez utiliser n'importe quel aspect de partout au lieu de pierres précieuses.
  2. Les utilisateurs de facettes n'a pas à connaître de pierre gemme de la structure, comme dans la pourrait être l'avant-déclarée et utilisée par PIMPL-modèle.
  3. D'autres classes peuvent se référer à facette plutôt des pierres précieuses - c'est la réponse à votre question sur la façon d'exposer limitée nubmer de méthodes à la classe spécifiée.

Espérons que cette aide. Vous vous le souhaitez, je peux poster des exemples de code pour illustrer cette tendance est plus clairement.

EDIT: Voici le code:

class Foo1; // This is all the client knows about Foo1
class PFoo1 { 
private: 
 Foo1* foo; 
public: 
 PFoo1(); 
 PFoo1(const PFoo1& pf); 
 ~PFoo(); 
 PFoo1& operator=(const PFoo1& pf); 

 void DoSomething(); 
 void DoSomethingElse(); 
}; 
class Foo1 { 
friend class PFoo1; 
protected: 
 Foo1(); 
public: 
 void DoSomething(); 
 void DoSomethingElse(); 
}; 

PFoo1::PFoo1() : foo(new Foo1) 
{} 

PFoo1::PFoo(const PFoo1& pf) : foo(new Foo1(*(pf
{} 

PFoo1::~PFoo() 
{ 
 delete foo; 
} 

PFoo1& PFoo1::operator=(const PFoo1& pf) 
{ 
 if (this != &pf) { 
  delete foo; 
  foo = new Foo1(*(pf.foo)); 
 } 
 return *this; 
} 

void PFoo1::DoSomething() 
{ 
 foo->DoSomething(); 
} 

void PFoo1::DoSomethingElse() 
{ 
 foo->DoSomethingElse(); 
} 

Foo1::Foo1() 
{ 
} 

void Foo1::DoSomething() 
{ 
 cout << "Foo::DoSomething()" << endl; 
} 

void Foo1::DoSomethingElse() 
{ 
 cout << "Foo::DoSomethingElse()" << endl; 
} 

EDIT2: Votre classe Toto1 pourrait être plus complexe, par exemple, il contient deux méthodes autres:

void Foo1::DoAnotherThing() 
{ 
 cout << "Foo::DoAnotherThing()" << endl; 
} 

void Foo1::AndYetAnother() 
{ 
 cout << "Foo::AndYetAnother()" << endl; 
} 

Et qu'ils sont accessibles via class PFoo2

class PFoo2 { 
    private: 
     Foo1* foo; 
    public: 
     PFoo2(); 
     PFoo2(const PFoo1& pf); 
     ~PFoo(); 
     PFoo2& operator=(const PFoo2& pf); 

     void DoAnotherThing(); 
     void AndYetAnother(); 
    };
void PFoo1::DoAnotherThing() 
    { 
     foo->DoAnotherThing(); 
    } 

    void PFoo1::AndYetAnother() 
    { 
     foo->AndYetAnother(); 
    } 

Ces méthodes ne sont pas en PFoo1 classe, de sorte que vous ne peut accéder à travers elle. De cette façon, vous pouvez diviser le comportement de l' Foo1 à deux (ou plus) facettes PFoo1 et PFoo2. Ces facettes classes pourrait être utilisé dans des endroits différents, et leur appelant shoudn pas être au courant de Toto1 mise en œuvre. C'est peut-être pas ce que vous voulez vraiment, mais ce que vous voulez, c'est impossible pour le C++, et c'est un travail-aroud, mais peut-être trop verbeux...

0voto

Staffan Points 1327

Quelque chose de semblable pour le code ci-dessous va vous permettre de contrôler soigneusement les parties de votre vie privée en état de vous rendre publics par le biais de l' friend mot-clé.

class X {
  class SomewhatPrivate {
    friend class YProxy1;

    void restricted();
  };

public:
  ...

  SomewhatPrivate &get_somewhat_private_parts() {
    return priv_;
  }

private:
  int n_;
  SomewhatPrivate priv_;
};

MAIS:

  1. Je ne pense pas que cela en vaut la peine.
  2. La nécessité d'utiliser l' friend mot clé peut suggérer que votre conception est erronée, peut-être il ya une façon que vous pouvez faire ce que vous avez besoin sans elle. J'essaie de l'éviter, mais si elle rend le code plus lisible, maintenable ou réduit le besoin de code réutilisable que je l'utilise.

EDIT: Pour moi, le code ci-dessus est (habituellement) une abomination qui doit généralement pas être utilisé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X