Почему защищенный член суперкласса не может быть доступен в функции подкласса при передаче в качестве аргумента?

Я получаю ошибку компиляции, о которой я немного смущен. Это на VS2003.

ошибка C2248: "A:: y": не может получить доступ к защищенному члену, объявленному в классе "A"

class A
{
public:
  A() : x(0), y(0) {}
protected:
  int x;
  int y;
};

class B : public A
{
public:
  B() : A(), z(0) {}
  B(const A& item) : A(), z(1) { x = item.y;}
private:
  int z;
};

Проблема заключается в x = item.y;

Доступ определяется как защищенный. Почему конструктор класса B не имеет доступа к A:: y?

Ответы

Ответ 1

Из-за этого:

class base_class
{
protected:
    virtual void foo() { std::cout << "base::foo()" << std::endl; }
};

class A : public base_class
{
protected:
    virtual void foo() { std::cout << "A::foo()" << std::endl; }
};

class B : public base_class
{
protected:
    virtual void foo() { std::cout << "B::foo()" << std::endl; }

public:
    void bar(base_class *b) { b->foo(); }
};

Если это было законно, вы могли бы сделать это:

A a;
B b;
b.bar(&a);

И вы будете называть членом protected из A из B, что не разрешено.

Ответ 2

В других ответах объясняются причины, по которым ваш объект B не может получить доступ к защищенным частям A в вашем примере, хотя B 'is-a' A. Конечно, самый простой способ исправить эту проблему - сделать части A you want access to public` или общедоступными методами доступа.

Однако вы можете решить, что это неправильно (или вы не можете контролировать определение A). Вот несколько советов, которые помогут вам решить проблему, в порядке возрастания порядка контроля доступа A. Обратите внимание, что во всех этих обходных методах предполагается, что class A имеет возможность копирования.

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

class B1 : public A
{
public:
  B1() : A(), z(0) {}
  B1(const A& item) : A(item), z(1) {
    // fix up the A sub-object that was copy constructed 
    //  not quite the way we wanted
    x = y;
    y = 0;
  }
private:
  int z;
};

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

В следующем примере создается временный объект B, который имеет точный дубликат объекта A, к которому мы хотим получить доступ. Затем мы можем использовать временный объект B для доступа к защищенным элементам:

class B2 : public A
{
public:
  B2() : A(), z(0) {}
  B2(const A& item) : A(), z(1) {
    // create a special-use B2  object that can get to the 
    //  parts of the A object we want access to
    B2 tmp( item, internal_use_only);

    x = tmp.y;  // OK since tmp is of type B
  }

private:
  int z;

  // create a type that only B2 can use as a 
  //    'marker' to call a special constructor 
  //    whose only purpose in life is to create
  //    a B object with an exact copy of another
  //    A sub-object in it
  enum internal_use {
    internal_use_only
  };
  B2( const A& item, internal_use marker) : A(item), z(0) {};
};

Я считаю, что решение будет немного менее запутанным, чем первое, но оно все еще запутывает (на мой взгляд). Наличие ублюдковой версии объекта B только для того, чтобы добраться до частей объекта A, который мы хотим, является нечетным.

Мы можем что-то с этим сделать, создав специальный прокси для объектов A, который дает доступ, который мы хотим. Обратите внимание, что это "самый подрывный" обходной путь, потому что это то, что может сделать любой класс, чтобы попасть в защищенные части A, даже если они не являются подклассами A. В случае класса B существует некоторая легитимность доступа к защищенным частям объектов A, так как B is-a A, и, как мы уже видели, существуют обходные пути, которые позволяют нам получить доступ, который использует только права, которые class B уже имеет, поэтому я рассматриваю это более чистую версию этих обходных решений в случае class B.

class B3 : public A
{
public:
  B3() : A(), z(0) {}
  B3(const A& item) : A(), z(1) { 
    // a special proxy for A objects that lets us
    //  get to the parts of A we're interested in
    A_proxy tmp( item);
    x = tmp.get_y();
  }

private:
  int z;

    class A_proxy : public A
    {
    public:
        A_proxy( const A& other) : A(other) {};
        int get_x() {return x;};
        int get_y() {return y;};
    };

};

Ответ 3

Документация IBM лучше всего описывает ее:

Защищенный нестатический базовый класс к члену могут быть доступны члены и друзей любых классов, полученных из этот базовый класс, используя один из следующее:

  • Указатель на прямо или косвенно полученный класс
  • Ссылка на прямой или косвенно производный класс
  • Объект прямого или косвенного производного класса

Таким образом, используя ваш пример выше в качестве основы:

B::B(const A& item) : A(), z(1) {
  // NOT OK because `item` is not a reference to the derived class B
  //int i = item.y; 

  // OK because `item` reinterpreted as a reference to the derived class B
  // Do not do this (bad!) -- for illustrative purposes only
  int i = reinterpret_cast< const B& >(item).y;

  // OK because it is equivalent to `this->x = i`,
  //  where `this` is a pointer to the derived class B
  x = i;
}