web-dev-qa-db-fra.com

subtile erreur d'héritage C ++ avec des champs protégés

Vous trouverez ci-dessous un exemple subtil d'accès au champ protégé d'une instance x. B est une sous-classe de A, donc toute variable de type B est également de type A. Pourquoi B :: foo () peut-il accéder au champ x de b, mais pas au champ x de a?

class A {
protected:
  int x;
};

class B : public A {
protected:
  A *a;
  B *b;
public:
  void foo() {
    int u = x;     // OK : accessing inherited protected field x
    int v = b->x;  // OK : accessing b's protected field x
    int w = a->x;  // ERROR : accessing a's protected field x
  }
};

Voici l'erreur que j'obtiens avec g ++

$ g++ -c A.cpp
A.cpp: In member function ‘void B::foo()’:
A.cpp:3: error: ‘int A::x’ is protected
A.cpp:14: error: within this context
43
wcochran

Puisque B est hérité publiquement de A, les membres protégés de A deviennent les membres protégés de B, afin que B puisse accéder à ses membres protégés comme d'habitude à partir de ses fonctions membres. Autrement dit, les objets de B peuvent accéder aux membres protégés de B à partir de ses fonctions membres.

Mais les membres protégés de A ne sont pas accessibles en dehors de la classe, en utilisant un objet de type A.

Voici le texte pertinent de la norme (2003)

11.5 Accès membre protégé [class.protected]

Lorsqu'un ami ou une fonction membre d'une classe dérivée fait référence à une fonction membre non statique protégée ou à un membre de données non statique protégé d'une classe de base, une vérification d'accès s'applique en plus de celles décrites précédemment dans la clause 11.102) Sauf lors de la formation d'un pointeur vers un membre (5.3 .1), l'accès doit se faire via un pointeur, une référence ou un objet de la classe dérivée elle-même (ou de toute classe dérivée de cette classe) (5.2.5). Si l'accès doit former un pointeur sur le membre, le spécificateur de nom imbriqué doit nommer la classe dérivée (ou toute classe dérivée de cette classe).

Et l'exemple découle de la norme (2003) elle-même:

[Example:

class B {
  protected:
  int i;
  static int j;
};

class D1 : public B {
};

class D2 : public B {
  friend void fr(B*,D1*,D2*);
  void mem(B*,D1*);
};

void fr(B* pb, D1* p1, D2* p2)
{
  pb->i = 1; // ill-formed
  p1->i = 2; // ill-formed
  p2->i = 3; // OK (access through a D2)
  p2->B::i = 4; // OK (access through a D2, even though naming class is B)
  int B::* pmi_B = &B::i; // ill-formed
  int B::* pmi_B2 = &D2::i; // OK (type of &D2::i is int B::*)
  B::j = 5; // OK (because refers to static member)
  D2::j =6; // OK (because refers to static member)
}
void D2::mem(B* pb, D1* p1)
{
  pb->i = 1; // ill-formed
  p1->i = 2; // ill-formed
  i = 3; // OK (access through this)
  B::i = 4; // OK (access through this, qualification ignored)
  int B::* pmi_B = &B::i; // ill-formed
  int B::* pmi_B2 = &D2::i; // OK
  j = 5; // OK (because j refers to static member)
  B::j = 6; // OK (because B::j refers to static member)
}
void g(B* pb, D1* p1, D2* p2)
{
  pb->i = 1; // ill-formed
  p1->i = 2; // ill-formed
  p2->i = 3; // ill-formed
}
—end example]

Remarque dans l'exemple ci-dessus fr() est une fonction amie de D2, mem() est une fonction membre de D2 Et g() n'est ni un ami, ni une fonction membre.

25
Nawaz

Considérer:

class A {
protected:
  int x;
};

class C : public A
{
};

class B : public A {
protected:
  unique_ptr<A> a;
public:
  B() : a(new C) // a now points to an instance of "C"
  { }

  void foo() {
    int w = a->x;  // B accessing a protected member of a C? Oops.
  }
};
14
Billy ONeal

Dans héritage public:
Tous les Public members Des classe de base deviennent Public Members Des classe dérivée &
Tous les Protected members Des classe de base deviennent Protected Members Des Derived Class.

Selon la règle ci-dessus:
membre protégé x de A devient membre protégé de la classe B.

class B Peut accéder à ses propres membres protégés dans sa fonction membre foo mais il ne peut accéder qu'aux membres de A à travers lesquels il a été dérivé pas tous A classes.

Dans ce cas, class B Contient un pointeur Aa, il ne peut pas accéder aux membres protégés de cette classe contenue.

Pourquoi la B::foo() peut-elle accéder aux membres du pointeur class Bb?

La règle est:
En C++, le contrôle d'accès fonctionne par classe, pas par objet.
Ainsi, une instance de class B Aura toujours accès à tous les membres d'une autre instance de class B.

Un exemple de code, qui illustre la règle:

#include<iostream>

class MyClass 
{
    public: 
       MyClass (const std::string& data) : mData(data) 
       {
       }

       const std::string& getData(const MyClass &instance) const 
       {
          return instance.mData;
       }

    private:
      std::string mData;
};

int main() {
  MyClass a("Stack");
  MyClass b("Overflow");

  std::cout << "b via a = " << a.getData(b) << std::endl;
  return 0;
}
3
Alok Save

Pourquoi B :: foo () peut-il accéder au champ x de b, mais pas au champ x de a?

Un membre protégé n'est accessible qu'aux autres membres de la même classe (ou classes dérivées).

b->x Pointe vers un membre protégé d'une instance de classe B (par héritage), afin que B::foo() puisse y accéder.

a->x Pointe vers un membre protégé d'une instance de classe A, donc B::foo() ne peut pas y accéder.

1
firyice

La classe B n'est pas identique à la classe A. C'est pourquoi les membres de la classe B ne peuvent pas accéder aux membres non publics de la classe A.

D'autre part, la classe Bdérive publiquement de la classe A, donc la classe B a maintenant un membre (protégé) x auquel tout membre de la classe B peut accéder.

0
Kerrek SB

Commençons par le concept de base,

class A {
protected:
   int x;
};

class B : public A {
public:
  void foo() {
    int u = x;     // OK : accessing inherited protected field
  }
};

Puisque l'enfant hérite d'un parent, l'enfant obtient x. Par conséquent, vous pouvez accéder à x directement dans la méthode foo () de l'enfant. C'est le concept de variables protégées. Vous pouvez accéder directement aux variables protégées du parent dans l'enfant. Remarque: Ici, je dis que vous pouvez accéder à x directement mais pas via l'objet de A! Quelle est la différence ? Étant donné que x est protégé, vous ne pouvez pas accéder aux objets protégés de A en dehors de A. Peu importe où il se trouve - s'il est principal ou enfant. C'est pourquoi vous ne pouvez pas accéder de la manière suivante

class B : public A {
protected:
  A *a;
public:
  void foo() {
    int u = x;     // OK : accessing inherited protected field x
    int w = a->x;  // ERROR : accessing a's protected field x
  }
};

Voici un concept intéressant. Vous pouvez accéder à une variable privée d'une classe en utilisant son objet avec dans la classe!

class dummy {
private : 
int x;
public:
  void foo() {
    dummy *d;
    int y = d->x; // Even though x is private, still you can access x from object of d - But only with in this class. You cannot do the same outside the class. 
  }
};

// Il en va de même pour les variables protégées, d'où vous pouvez accéder à l'exemple suivant.

class B : public A {
protected:
  A *a;
  B *b;
public:
  void foo() {
    int u = x;     // OK : accessing inherited protected field x
    int y = b->x;   // OK : accessing b's protected field x
    int w = a->x;  // ERROR : accessing a's protected field x
  }
};

J'espère que cela explique :)

C++ est une programmation orientée objet complète, où Java est purement orienté objet :)

0
mac