l'accès à un membre protégé d'une classe de base dans une autre sous-classe

pourquoi cette compilation:

class FooBase
{
protected:
    void fooBase(void);
};

class Foo : public FooBase
{
public:
    void foo(Foo& fooBar)
    {
        fooBar.fooBase();
    }
};

mais cela ne fonctionne pas?

class FooBase
{
protected:
    void fooBase(void);
};

class Foo : public FooBase
{
public:
    void foo(FooBase& fooBar)
    {
        fooBar.fooBase();
    }
};

d'une part C++ accorde l'accès aux membres privés/protégés pour toutes les instances de cette classe, mais d'autre part il ne donne pas l'accès aux membres protégés d'une classe de base pour toutes les instances d'une sous-classe. Cela me semble assez incohérent.

j'ai testé la compilation avec VC++ et avec ideone.com et les deux compilent le premier mais pas le second code.

34
demandé sur iammilind 2012-07-24 17:20:47

7 réponses

Quand foo reçoit un FooBase référence, le compilateur ne sait pas si l'argument est un descendant de Foo, donc il doit supposer que ce n'est pas le cas. Foo a accès aux membres protégés hérités de autres Foo objets, pas tous les autres cours.

réfléchir à ce code:

class FooSibling: public FooBase { };

FooSibling sib;
Foo f;
f.foo(sib); // calls sib.fooBase()!?

Si Foo::foo peut appeler des membres protégés de l'arbitraire FooBase descendants, puis il peut appeler la méthode protégée de FooSibling, qui n'a pas de en relation directe avec les Foo. Ce n'est pas comment protéger l'accès est censé fonctionner.

Si Foo a besoin d'accéder aux membres protégés de tous FooBase les objets, pas seulement de ceux qui sont également connus pour être Foo descendants, puisFoo doit être un ami de FooBase:

class FooBase
{
protected:
  void fooBase(void);
  friend class Foo;
};
30
répondu Rob Kennedy 2012-07-24 13:42:33

C++ FAQ résume bien ce numéro:

[vous] avez le droit de faire vos propres poches, mais vous n'avez pas le droit de faire les poches de votre père ni celles de votre frère.

19
répondu h0b0 2016-07-25 12:30:09

Le point clé est que protected vous accorde l'accès à votre propre copie du Membre, pas à ces membres en autre objet. Il s'agit là d'une erreur courante, car le plus souvent nous généralisons et disons protected autorise l'accès au membre le type dérivé (sans indiquer explicitement que seulement à leurs propres bases...)

maintenant, C'est pour une raison, et en général vous ne devriez pas accéder au membre dans une branche différente de la hiérarchie, comme vous pourriez cassez les invariants dont dépendent les autres objets. Considérons un type qui effectue un calcul coûteux sur un grand membre de données (protégé) et deux types dérivés qui cache le résultat suivant différentes stratégies:

class base {
protected:
   LargeData data;
// ...
public:
   virtual int result() const;      // expensive calculation
   virtual void modify();           // modifies data
};
class cache_on_read : base {
private:
   mutable bool cached;
   mutable int cache_value;
// ...
   virtual int result() const {
       if (cached) return cache_value;
       cache_value = base::result();
       cached = true;
   }
   virtual void modify() {
       cached = false;
       base::modify();
   }
};
class cache_on_write : base {
   int result_value;
   virtual int result() const {
      return result_value;
   }
   virtual void modify() {
      base::modify();
      result_value = base::result(); 
   }
};

cache_on_read type capture les modifications aux données et marque le résultat comme non valide, de sorte que le prochain lire de la valeur recalcule. C'est une bonne approche si le nombre d'écritures est relativement élevé, comme nous l'avons seulement d'effectuer la calcul à la demande (c.-à-d. que les modifications multiples ne déclencheront pas de nouveaux calculs). cache_on_write calcule le résultat à l'avance, ce qui pourrait être une bonne stratégie si le nombre d'Écritures est faible, et vous voulez des coûts déterministes pour la lecture (pensez à une faible latence sur les lectures).

Maintenant, revenons au problème initial. Les deux stratégies de cache maintiennent un ensemble plus strict d'invariants que la base. Dans le premier cas, l'invariant supplémentaire est que cachedtrue seulement si data n'a pas été modifié après la dernière lecture. Dans le second cas, l'invariant supplémentaire est que result_value est la valeur de l'opération à tout moment.

si un troisième type dérivé a pris une référence à un base et accessible data écrire (si protected l'autoriserait à), alors il se briserait avec les invariants des types dérivés.

cela étant dit, la spécification de la langue est cassé (avis personnel) qu'il laisse une porte dérobée pour atteindre ce résultat. En particulier, si vous créez un pointeur de membre d'un membre à partir d'une base dans un type dérivé, l'accès est contrôlé par derived, mais le pointeur retourné est un pointeur de membre de l' base, qui peut être appliqué à base objet:

class base {
protected:
   int x;
};
struct derived : base {
   static void modify( base& b ) {
      // b.x = 5;                        // error!
      b.*(&derived::x) = 5;              // allowed ?!?!?!
   }
}
10
répondu David Rodríguez - dribeas 2012-07-24 15:21:29

Dans les deux exemples Foo hérite d'une méthode protégée fooBase. Cependant, dans votre premier exemple, vous essayez d'accéder aux données protégées méthode de la même classe (Foo::foo appelle Foo::fooBase), alors que dans le second exemple vous essayez d'accéder à une méthode protégée d'une autre classe qui n'est pas déclarée comme classe ami (Foo::foo essaie de l'appeler FooBase::fooBase, qui échoue, la dernière est protégée).

3
répondu Zeta 2012-07-24 13:25:06

dans le premier exemple vous passez un objet de type Foo, qui hérite évidemment de la méthode fooBase() et est donc capable de l'appeler. Dans le deuxième exemple, vous essayez d'appeler une fonction protégée, simplement ainsi, quel que soit le contexte dans lequel vous ne pouvez pas appeler une fonction protégée à partir d'une instance de classe où elle est déclarée ainsi. Dans le premier exemple, vous héritez de la méthode protégée fooBase, et vous avez donc le droit de l'appeler dans le contexte Foo

1
répondu Moataz Elmasry 2012-07-24 13:29:05

j'ai tendance à voir les choses en termes de concepts et de messages. Si votre méthode de FooBase s'appelait en fait "SendMessage" et Foo était "English Speakingperson" et FooBase était SpeakingPerson, votre protégé

1
répondu Sentinel 2012-07-24 14:00:36

hobo réponse vous pouvez chercher une solution de contournement.

si vous voulez que les sous-classes appellent fooBase méthode, vous pouvez le faire static. les méthodes statiques protégées sont accessibles par des sous-classes avec tous les arguments.

0
répondu yairchu 2017-05-23 11:33:26