Защищенные данные в родительском классе недоступны в дочернем классе? - PullRequest
11 голосов
/ 12 сентября 2009

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

Приведенный ниже фрагмент не компилируется в 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'

Что я делаю не так?

Ответы [ 4 ]

10 голосов
/ 12 сентября 2009

Согласно TC ++ PL, стр. 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 голосов
/ 12 сентября 2009

Стандарт C ++ говорит о защищенных нестатических членах на 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)

1 голос
/ 12 сентября 2009

Вы просто не должны копировать объект 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 ) {}
};
0 голосов
/ 12 сентября 2009

Конструктор 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...