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