Un exemple pour illustrer mon problème:
class Base { protected: int x{ 0 }; }; class DerivedClass1 : public Base { }; class DerivedClass2 : public DerivedClass1 { public: int getX() const { return x; } }; int main() { DerivedClass2 d2{}; int x{ d2.getX() }; return 0; }
Je peux accéder au membre protégé de Base
dans la classe DerivedClass2
, bien que le les membres protégés de Base
ne doivent être modifiés que dans DerivedClass1
. En héritant de la variable de Base
en DerivedClass1
une classe doit être formée, que la DerivedClass2
ne doit pas manipuler. En C #, cela est possible avec le mot-clé private protected
, mais comment pouvez-vous le gérer en C ++?
3 Réponses :
Vous pouvez déclarer le membre de données comme private
et utiliser les déclarations friend
pour spécifier explicitement les classes qui peuvent y accéder:
class Base { friend class DerivedClass1; private: int x = 0; }; class DerivedClass1 : public Base { void test() { cout << x; // OK: DerivedClass1 is a friend of Base } }; class DerivedClass2 : public DerivedClass1 { public: int getX() const { return x; // ERROR: x is a private member } };
p>
C'est vrai, merci. Ce serait une solution de compromis, bien entendu.
En effet, c'est aussi une solution possible. Son principal inconvénient est qu'il force un couplage fort entre Base
et Derived
, et en particulier, Base
a besoin de connaître tout son potentiel de premier degré dérivés. Pas vraiment OCP -friendly ;-)
C'est l'idée même de protected
de permettre à toutes les classes dérivées d'accéder à de tels membres.
Bien qu'il s'agisse d'une fonctionnalité de langage très flexible, elle crée une sérieuse faiblesse dans l'encapsulation, ce qui encourage la rupture du Principe de substitution de Liskov (plus précisément la contrainte d'historique, puisqu'une classe dérivée peut modifier l'état d'un objet de base sans passer par une primitive de classe de base).
Si vous voulez une encapsulation plus forte et un accès restrictif, vous devez vous rendre dans private
. Cela garantit que l'état interne d'une classe de base ne peut être accédé qu'à l'aide de l'interface publique. (Notez que s'il assure la contrainte d'historique, il ne garantit pas à lui seul le LSP). Et la conséquence est qu'aucune classe dérivée n'y a accès. Pas à la première dérivation, ni plus tard.
Ce que vous voulez, c'est une sorte d'intermédiaire: une encapsulation faible, mais pas trop faible. Cela n'existe pas en C ++. Et je ne suis pas sûr que cela renforcerait votre conception.
Mais si vous avez besoin de cette limitation à titre exceptionnel, il pourrait y avoir une solution de contournement, en jouant avec la recherche de nom:
class DerivedClass1 : public Base { private: using Base::x; // In DerivedClass1 you can still use x. }; // But it will fail to compile in Derived2
Mais personnellement, je ne conseillerais pas de suivre cette voie. Il est sujet aux erreurs (vous pourriez oublier le using
dans un frère dérivé). Les messages d'erreur du compilateur peuvent être trompeurs. Et de toute façon, le privé donne une conception plus robuste.
Hériter d'une base "privée":
class DerivedClass1 : private Base { };
Modifier: exposer des membres publics ou protégés dans Base via des membres publics ou protégés dans DerivedClass1 em >. DerivedClass1 a alors un contrôle complet sur ce que les membres de Base peuvent et ne peuvent pas être accédés par les classes héritant de DerivedClass1 .
Que ce soit une bonne solution dépend de la complexité de Base.
C'est bien sûr une solution, mais cela ne fonctionne que si la classe base
n'a pas de membres publics. THX:)
Vrai; ces membres doivent être exposés via des méthodes publiques ou protégées dans DerivedClass1.
Les données protégées sont presque toujours une mauvaise idée. Simplement, ne l'utilisez pas.
Merci pour la réponse rapide - hmm ok.
FWIW, le niveau d'accès C #
private protected
ne fait pas vraiment ce que vous voulez non plus.