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

Я получаю ошибку компиляции, в которой я немного запутался. Это на 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?

Ответы [ 3 ]

5 голосов
/ 01 апреля 2010

Это из-за этого:

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, что недопустимо.

3 голосов
/ 01 апреля 2010

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

Однако вы можете решить, что это неуместно (или вы не можете контролировать определение 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;};
    };

};
1 голос
/ 01 апреля 2010

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