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

Защищенные данные в родительском классе недоступны в дочернем классе?

Я запутался: я думал, что защищенные данные были прочитаны/записаны дочерними элементами данного класса в С++.

Ниже фрагмент не удается скомпилировать в MS Compiler

class A
{
protected:
  int data;
};

class B : public A
{
  public:

  B(A &a)
  {
    data = a.data;
  }
};

int main()
{
  A a;
  B b = a;
  return 0;
}

Сообщение об ошибке:

Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

demoFail.cpp
demoFail.cpp(12) : error C2248: 'A::data' : cannot access protected member declared in class 'A'
        demoFail.cpp(4) : see declaration of 'A::data'
        demoFail.cpp(2) : see declaration of 'A'

Что я делаю неправильно?

4b9b3361

Ответ 1

Согласно TС++ PL, pg 404:

Производный класс может получить доступ к элементам, защищенным базовым классом, только для объектов своего типа.... Это предотвращает тонкие ошибки, которые в противном случае возникали бы, когда один производный класс искажал данные, принадлежащие другим производным классам.

Конечно, вот простой способ исправить это для вашего случая:

class A
{
protected:
    int data;
};

class B : public A
{
public:
    B(const A &a)
        : A(a)
    {
    }
};

int main()
{
    A a;
    B b = a;
    return 0;
}

Ответ 2

Стандарт С++ говорит об защищенных нестатических элементах в 11.5/1

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

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

class B : public A {
public:
  B(A &a){
    int A::*dataptr = &B::data;
    data = a.*dataptr;
  }
};

Конечно, этот код не рекомендуется делать, но показывает, что вы можете получить к нему доступ, если вам действительно нужно (я видел, как этот способ используется для печати std::stack, std::queue, std::priority_queue путем доступа к его защищенному члену контейнера c)

Ответ 3

Вы просто не должны копировать объект A в конструкторе B. Цель состоит в том, чтобы оставить инициализацию членов A его собственному конструктору:

struct A { 
  A( const A& a ): data( a.data ) {}
  protected: int data; 
};

struct B : public A {
  B( const A& a ): A( a ) {}
};

Ответ 4

Конструктор B частный. Если вы ничего не укажете, в классе модификатор по умолчанию является приватным (для структур он является общедоступным). Итак, в этом примере проблема заключается в том, что вы не можете построить B. Когда вы добавляете public в конструктор B возникает проблема с анотером:

B имеет право изменять часть A, из которой он получается, но не является другим A, как в этом случае.

Вы можете сделать следующее:

class A
{
public:
  A()
      : data(0)
  {
  }
  A(A &a)
  {
    data = a.data;
  }
protected:
  int data;
};

class B : public A
{
public:
  B(A &a)
      : A(a)
  {
  }
};

int main()
{
  A a;
  B b = a;
  return 0;
}