Click to See Complete Forum and Search --> : overzealous protection rules


blueimpb
July 12th, 2006, 10:12 AM
The following doesn’t compile:



struct A
{
A* partner;


void doodah()
{ ++partner->value; } //this is legal
protected:
int value;
};

struct B: public A
{
void foobar()
{ partner->value += 2; } //but this isn’t
};

The error is that B::foobar() can’t access partner->value, due to protection, even though B is an A. Now I'm not a language lawyer, but honestly this should work. B is an A; and A can access the protected members of other A's. But B can't??

matthewp
July 12th, 2006, 10:33 AM
I'm not entirely sure why but this works:


struct B: public A
{
void foobar()
{
value += 2; }
};



Comeau gives this error for the original code
"ComeauTest.c", line 19: error: protected member "A::value" is not accessible through a "A" pointer or object partner->value += 2;

which is more enlightening than Visual Studio.

-Matt

blueimpb
July 12th, 2006, 10:35 AM
Well that works because you're accessing B's *own* value member. But I don't want to access that - I want to access the value member of "partner" which is a member of B inherited from A.

Paul McKenzie
July 12th, 2006, 10:38 AM
The error is that B::foobar() can’t access partner->value, due to protection, even though B is an A.In the foobar() function, what is the value of this? What is the value of partner? There's your reason -- different objects. You're looking at this at the class level, when you should be looking at it at the object level.

You have two objects there, this and partner -- you don't have one object, regardless of where you placed the declaration of partner.

Regards,

Paul McKenzie

blueimpb
July 12th, 2006, 11:54 AM
I know you get a lot of newb's posting but yes, I realize that "partner" and "this" are different. Did the fact that I named it "partner" not make that obvious?

It's extremely common to have a pointer to ANOTHER object of your OWN type. Think about that for a minute...then remember the basic definition of a linked list.

Now imagine the following:

class Node
{
public:
virtual void doSomething() {}
protected:
Node* child;
Node* parent;
};

class Int_Node : public Node
{
int data;
public:
virtual void doSomething()
{
//first, check list validity
if (parent->child != (Node*)this)
cout << "Oh No!";
....
}
};

see the idea?

Graham
July 12th, 2006, 12:22 PM
There are three levels of access for precisly this reason. Public is accessible by all, protected by derived classes, and private only by the class in which it is declared.

The statement "B IS-A A" is a meta-semantic statement about the use of the public inheritance relationship: it is not a rule of the standard. As far as the standard is concerned, derived classes can only access public and protected members of their base classes. You cannot access the "partner" member of the base class, because it's private to that class. You can access this->value, because value is protected, but you can't access "partner" at all.

This is exactly how it should be.

In your last example, there is no error, since both members are protected and therefore accessible by the derived class. However, this is not good design, since you have introduced excessive coupling between the two classes and you're heading for maintenance problems. The code you've put into the derived class to compare the pointers would be better off in the base class, since it's a base class operation that is dependent on the implementation details of the base class and should be of no concern to the derived class.

NMTop40
July 13th, 2006, 07:00 AM
The second example is not valid as Int_Node cannot access parent->child.

You are not allowed to access protected members of base-class instances other than your own. The reason is simple - it might not be your type so access should be restricted.

In the example above, Node::parent may not be your type. It could be float_Node. In the first example there could be another class C that derives from A and partner could be of type C.

What you should be doing is not having protected access to members, only to methods. Some of these methods may be virtual so you can override it for the class in question. Thus if partner is of type C, when you call the method from B (a method in your own A, not in partner) it will actually invoke a method defined in C, its own class.

Graham
July 14th, 2006, 03:00 AM
The second example is not valid as Int_Node cannot access parent->child.
Oops. Missed that one. :blush:

TomWidmer
July 14th, 2006, 05:22 AM
You are only allowed to access protected members of a base class through your own type. This is to stop you from breaking encasulation for other classes derived from that base class. e.g.
#include <assert.h>
class A {
protected: int i;
};
class B: public A {
void mutate(A& a) {
a.i = -1;
}
};
class C: public A {
public:
C() {
i = 1;
}
~C() {
assert (i == 1);
}
};

int main() {
C c;
B b;
b.mutate(c);
}
Here, if that were allowed, b would be allowed to break an invariant of class C, namely that i == 1.

For your code, that does of course suggest a solution:
struct B: public A
{
void foobar()
{
if (B* p = dynamic_cast<B*>(partner))
{
//ok, it's a B (not a C, D or E), so we are allowed to modify its protected parts
//since we know how to modify Bs, given that we are a B!
p->value += 2;
}
}
};