Подтвердить что ты не робот

Тонкая ошибка наследования С++ с защищенными полями

Ниже представлен тонкий пример доступа к защищенному экземпляром поле x. B является подклассом A, поэтому любая переменная типа B также имеет тип A. Почему B:: foo() имеет доступ к полю b x, но не к полю x?

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 protected field x
    int w = a->x;  // ERROR : accessing a protected field x
  }
};

Вот ошибка, которую я получаю с 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
4b9b3361

Ответ 1

Так как B является публично унаследованным от A, защищенный член становится B-защищенным членом (-ами), поэтому B может обращаться к своим защищенным членам, как обычно, из его функций-членов. То есть объекты B могут получить доступ к защищенным членам B из своих функций-членов.

Но защищенные члены не могут быть доступны за пределами класса, используя объект типа A.

Вот соответствующий текст из Стандарта (2003)

11.5 Защищенный доступ к элементу [class.protected]

Когда функция друга или члена производного класса ссылается на защищенную нестатистическую функцию-член или защищенный нестатический член данных базового класса, проверка доступа применяется в дополнение к тем, которые описаны ранее в разделе 11.102). За исключением случаев, когда формируется указатель на член (5.3.1), , доступ должен быть через указатель на, ссылку или объект самого производного класса (или любого класса, полученного из этого класса) (5.2.5). Если доступ заключается в формировании указателя на член, спецификатор вложенного имени должен указывать производный класс (или любой класс, полученный из этого класса).

И пример следует из самого Стандарта (2003):

[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]

Примечание в приведенном выше примере fr() является дружной функцией D2, mem() является функцией-членом D2, а g() не является ни другом, ни функцией-членом.

Ответ 2

Рассмотрим:

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.
  }
};

Ответ 3

В Наследование на общественных началах:
Все Public members базового класса становятся Public members производного класса &
Все Protected members базового класса становятся Protected members Derived Class.

В соответствии с приведенным выше правилом:
защищенный член x от A становится защищенным членом класса B.

class B может получить доступ к своим защищенным членам в своей функции-члене foo, но он может обращаться только к членам A, через которые он был получен не всеми классами A.

В этом случае class B содержит указатель A A, он не может получить доступ к защищенным членам этого содержащегося класса.

Почему B::foo() может получить доступ к элементам содержащегося указателя class B B?

Правило:
В С++ управление доступом работает только для каждого класса, а не для каждого объекта.
Таким образом, экземпляр class B всегда будет иметь доступ ко всем членам другого экземпляра class B.

Пример кода, который демонстрирует правило:

#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;
}

Ответ 4

Почему B:: foo() получает доступ к полю b x, но не к полю x?

Защищенный член может быть доступен только другим членам того же класса (или производных классов).

b->x указывает на защищенный член экземпляра класса B (через наследование), поэтому B::foo() может получить к нему доступ.

a->x указывает на защищенный член экземпляра класса A, поэтому B::foo() не может получить к нему доступ.

Ответ 5

Класс B не идентичен классу A. Поэтому члены класса B не могут получить доступ к непубличным членам класса A.

С другой стороны, класс B выводится публично из класса A, поэтому класс B теперь имеет (защищенный) член x, к которому может получить доступ любой член класса B.

Ответ 6

Давайте начнем с базовой концепции,

class A {
protected:
   int x;
};

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

Поскольку дочерний элемент наследует родителя, дочерний элемент получает x. Следовательно, вы можете получить доступ к x непосредственно в foo() методе child. Это концепция защищенных переменных. Вы можете напрямую обращаться к защищенным переменным родительского элемента. Примечание. Здесь я говорю, что вы можете получить доступ к x напрямую, но не через объект A! Какая разница? Поскольку x защищен, вы не можете получить доступ к защищенным объектам вне A. Не имеет значения, где он находится - если его основной или дочерний. Вот почему вы не можете получить доступ следующим образом

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 protected field x
  }
};

Вот интересная концепция. Вы можете получить доступ к частной переменной класса, используя свой объект в классе!

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. 
  }
};

//То же самое для защищенных переменных. Если вы можете получить доступ к следующему примеру.

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 protected field x
    int w = a->x;  // ERROR : accessing a protected field x
  }
};

Надеюсь, он объяснит:)

С++ - это полное объектно-ориентированное программирование, где, поскольку Java является чисто объектно-ориентированным:)