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

StackOverflow https://stackoverflow.com/questions/1414506

  •  06-07-2019
  •  | 
  •  

Вопрос

Я запутался: я думал, что защищенные данные были доступны для чтения / записи детям данного класса в 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'

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

Это было полезно?

Решение

Согласно TC ++ PL, стр. 404:

  

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

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

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

Другие советы

Стандарт 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 )

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

Конструктор 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;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top