Почему я не могу получить доступ к защищенному члену из экземпляра базового класса?

Скажем, у меня есть этот код:

class foo{
  protected:
  int a;
};

class bar : public foo {
  public:
  void copy_a_from_foo(foo& o){
    a = o.a; // Error
  }
  void copy_a_from_bar(bar& o){
    a = o.a; // OK
  }
};

int main(){
  bar x;
  foo y;
  bar z;
  x.copy_a_from_foo(y);
  x.copy_a_from_bar(z);
}

здесь class bar не имеет проблем с доступом к защищенному члену a из другого экземпляра того же класса, но когда я пытаюсь сделать то же самое с экземпляром базового класса foo, компилятор дает мне ошибку говоря, что a защищен. Что стандарт должен сказать об этом?

ошибка

prog.cpp: In member function 'void bar::copy_a_from_foo(foo&)':
prog.cpp:3:7: error: 'int foo::a' is protected
   int a;
       ^
prog.cpp:9:11: error: within this context
     a = o.a;

PS: Я рассмотрел этот вопрос, но это не совсем то же самое: я пытаюсь получить доступ к защищенному члену из производного класса.

Ответ 1

Вы можете получить доступ к элементам protected базового класса только через указатель или ссылку на объект производного типа.

Если вы измените

void copy_a_from_bar(bar& o){
  a = o.a;
}

к

void copy_a_from_bar(bar& o){
  foo& foo_ref = o;
  a = o.a;        // OK. Accessing it through `bar&`
  a = foo_ref.a;  // Not OK. Accessing it through `foo&`
}

вы увидите ту же ошибку.

Этот ответ "SO" дает указание, почему предоставление доступа к членам protected базового класса будет потенциальным нарушением статуса protected базовых классов.

Скажите, что у вас есть:

class baz : public foo
{
   void update_a(foo& f)
   {
      f.a = 20;
   }
};

и используйте:

bar x;
baz z;
z.update_a(x);

Если это разрешено, baz сможет изменить значения элементов bar. Это не хорошо.

Ответ 2

protected означает, что он может быть доступен как член в производных классах. Он не предоставляет неограниченный доступ к производным классам.

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

Контракт - это обещание о состояниях членов. Некоторые примеры контрактов, которые могут быть вам знакомы, находятся во внутренних строках строки: size содержит длину строки в буфере, а buffer[size] содержит нуль (здесь тонна технических проблем, но они несущественны), Также, что буфер всегда указывает на нуль или действительную строку с завершающим нулем, с уникальным правом собственности. Класс строки работает жесткий, чтобы убедиться, что, независимо от того, что все эти вещи являются правдой. (строка фактически не имеет ни одного из этих контрактов, потому что члены являются частными, это просто пример)

Ответ 3

Это распространенное заблуждение относительно того, что означает protected. Это не означает, что вы можете получить доступ к члену любого объекта базового типа из производного типа, но только в подобъектах, которые являются частью объекта производного типа.

Обоснование заключается в том, что вы получаете контроль над участником для своего объекта, где вы знаете, что делаете, но без труда перепутаете состояние других объектов. Рассмотрим этот пример, где CachedDoubleValue поддерживает кешированное значение с двойным значением в базовом объекте:

class Base {
protected:
   int x;
};
class CachedDoubleValue : public Base {
   int y;
public:
   void set(int value) {
      x = value;
      y = 2 * x;
   }
};
class AnotherDerived : public Base {
public:
    static void setX(Base &b, int value) {
       b.x = 10;
    }
};
int main() {
    CachedDoubleValue a;
    a.set(1);             // a.x = 1, a.y = 2
    AnotherDerived::modifyX(a, 10);
        // Invariant broken: a.x = 10, a.y = 2; a.x != 2 * a.y
}