I get a compile error, which I'm slightly confused about. This is on VS2003.
error C2248: 'A::y' : cannot access protected member declared in class '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;
};
The problem is with x = item.y;
The access is specified as protected. Why doesn't the constructor of class B have access to A::y?
It's because of this:
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(); }
};
If that were legal, you could do this:
A a;
B b;
b.bar(&a);
And you'd be calling a protected
member of A from B, which isn't allowed.
The other answers explain the reasoning behind preventing your B
object from accessing the protected parts of A
in your example, even though B
'is-a' A
. Of course, the easiest way to fix this problem is to make the parts of A you want access to
public` or have publicly accessible accessor methods.
However you might decide that's inappropriate (or you might not have control over the definition of A
). Here are some suggestions to let you work around the problem, in increasing order of subverting A
's access control. Note that all of these workarounds assume that class A
is copy-constructable.
In the first case, you simply use the copy constructor for A
to set up an initial state for that part of the B
object, then fix it up afterward:
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;
};
I find that incredibly confusing and probably very error prone (assuming that we want the A
sub-object in the B
object to be different than the A
object being passed to the constructor - an unusual situation, but it's what was given in the problem). However, the fact that it can be done gives some justification for the more subversive examples that follow...
The next example creates a temporary B
object that has an exact duplicate of the A
object we want access to. We can then use the temporary B
object to get to the items that were protected:
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) {};
};
I find that solution to be a bit less confusing than the first, but it's still confusing (in my opinion). Having a bastard version of of B object just to get to the parts of the A object we want is odd.
We can do something about that by creating a special proxy for A
objects that gives the access we want. Note that this is the 'most subversive' workaround because it's something that any class could do to get to protected parts of A
, even if they aren't sub-classes of A
themselves. In the case of the B
class, there's some legitimacy to getting to the protected parts of A
objects, since B
is-a A
, and as we've already seen there are workarounds that let us get access that use only rights that class B
already has, so I consider this a cleaner version of those workarounds in class B
's case.
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;};
};
};
IBM's documentation summarizes it best:
A protected nonstatic base class
member can be accessed by members and
friends of any classes derived from
that base class by using one of the
following:
- A pointer to a directly or indirectly derived class
- A reference to a directly or indirectly derived class
- An object of a directly or indirectly derived class
Thus, using your example above as the basis:
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;
}