2
votes

Comment empêcher l'accès aux membres protégés de la classe de base au deuxième niveau de la sous-classe?

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 commentaires

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.


3 Réponses :


1
votes

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>


2 commentaires

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 ;-)



4
votes

Quelles sont les conséquences de protected?

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).

Comment éviter ses inconvénients?

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.

Avez-vous besoin d'une protection privée?

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

Démo en ligne

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.


0 commentaires

0
votes

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.


2 commentaires

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.