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
}
};
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?
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*/
}
};
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.
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.)
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.
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:
Hérité de FooA
, de cette façon, seules les méthodes protégées et publiques sont exposées.
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();