Why can't I freely use protected functions in derived classes?

J

Jace/TBL

Suppose I have the following situation:

class A
{
protected:
virtual void Func() = 0;
};

class B : public A
{
protected:
virtual void Func();
void Foo( A *bar );
};

void B::Foo( A *bar )
{
bar->Func();
}

I get an error on bar->Func(), cause apparently I cannot call
protected functions on other instances than this. This surprised me, I
thought 'protected' would only limit the accessibility to the class
where it's defined and derived classes. But it also seems to allow
access *within this instance only*.

Is there a reasonable explanation for this behaviour of protected?
 
P

Phlip

Jace/TBL wrote;
Suppose I have the following situation:

class A
{
protected:
virtual void Func() = 0;
};

class B : public A
{
protected:
virtual void Func();
void Foo( A *bar );
};

void B::Foo( A *bar )
{
bar->Func();
}

I get an error on bar->Func(), cause apparently I cannot call
protected functions on other instances than this. This surprised me, I
thought 'protected' would only limit the accessibility to the class
where it's defined and derived classes. But it also seems to allow
access *within this instance only*.

This should be in the FAQ.

It's not the "derived class" doing it. You'd get the same effect with no
inheritance.

Simply put, you can only access a protected thing if you could have said
this->thing . The meaning of "protected" is "only my own descendents can
access me, and only access their own instance."

Things that can't say "this->" behave as if they could.
Is there a reasonable explanation for this behaviour of protected?

When you change a protected method, would you like to know _exactly_ who the
change affects?
 
P

Phlip

Jace/TBL wrote;
This should be in the FAQ.

It's not the "derived class" doing it. You'd get the same effect with no
inheritance.

Simply put, you can only access a protected thing if you could have said
this->thing . The meaning of "protected" is "only my own descendents can
access me, and only access their own instance."

Things that can't say "this->" behave as if they could.

I thought of another way to say this:

Suppose you could write this->meProtected(). If you say 'that = this', then
you can't compile that->meProtected(). Anything except our own explicit or
implicit 'this' will fail.
 
D

David Harmon

On Fri, 29 Oct 2004 14:31:07 GMT in comp.lang.c++, "Phlip"
It's not the "derived class" doing it. You'd get the same effect with no
inheritance.

Simply put, you can only access a protected thing if you could have said
this->thing . The meaning of "protected" is "only my own descendents can
access me, and only access their own instance."

Things that can't say "this->" behave as if they could.

That is all bullshit.
You can access protected members of any instance of your own class.

You cannot access them via a (base*) pointer, because there is no reason
to believe it points to an instance of your own class. It may point to a
different derived class, in which case for you to access his protected
members would be a violation of encapsulation.

void B::Foo( A *bar ) // no access to protected members of bar

void B::Foo( B *bar ) // OK for access to protected members of bar
 
A

Andrey Tarasevich

Jace/TBL said:
...
I get an error on bar->Func(), cause apparently I cannot call
protected functions on other instances than this. This surprised me, I
thought 'protected' would only limit the accessibility to the class
where it's defined and derived classes. But it also seems to allow
access *within this instance only*.
...

That's not exactly the case. In general it is correct to say that access
control in C++ is implemented on per-class basis, not on per-object
(per-instance) basis. However, 'protected' access stands out a bit
because it has some specifics that sometimes give it a false
"per-instance" feel in some contexts. You just gave an example of such
context.

However, this is not really an attempt to implement "per-instance"
access control. The key element of protected member access specification
in this case is the following (see 11.5/1): "Except when forming a
pointer to member, the access must be trough a pointer to, reference to,
or object of the derived class itself (or ant class derived from that
class)".

In your case the following member function would compile

void B::Foo(B *bar)
{
bar->Func();
}

This demonstrates the fact that protected member access control
specification is not really trying to implement a "per-instance" access
control. What it is trying to do though is to make sure that the
protected member you are trying to access from a method of class 'B'
does actually "belong" to class 'B' (as a subobject).
 
A

Arijit

Suppose I have the following situation:

class A
{
protected:
virtual void Func() = 0;
};

class B : public A
{
protected:
virtual void Func();
void Foo( A *bar );
};

void B::Foo( A *bar )
{
bar->Func();
}

I get an error on bar->Func(), cause apparently I cannot call
protected functions on other instances than this. This surprised me, I
thought 'protected' would only limit the accessibility to the class
where it's defined and derived classes. But it also seems to allow
access *within this instance only*.

Consider the following code:

class C : public A
{
protected:
virtual void Func();
};

int main()
{
B b;
C c;

b.Foo(&C);
}

C is a separate class. It knows nothing about B. So how can you call
C::Func, a protected member, in B ? In other words, bar need not be of
type B. If you change the code to

void Foo(B* bar)

it will work, because bar is guranteed to be of type B.

-Arijit
 
J

Jace/TBL

David Harmon said:
You cannot access them via a (base*) pointer, because there is no reason
to believe it points to an instance of your own class.

But I don't - I only believe it points to an instance of my base
class. Which is, by definition, supposed to implement Foo().
It may point to a different derived class, in which case for you
to access his protected members would be a violation of encapsulation.

Why? I consider putting a pure virtual Foo() in A to guarantee that
any derived class from A can do Foo(). How would making use of this in
B violate encapsulation?
 
J

Jace/TBL

Phlip said:
It's not the "derived class" doing it. You'd get the same effect with no
inheritance.

Simply put, you can only access a protected thing if you could have said
this->thing . The meaning of "protected" is "only my own descendents can
access me, and only access their own instance."

Things that can't say "this->" behave as if they could.

I always considered private, protected and public to work like this:

private = only my own class can access me
protected = only my own class and derived classes can access me
public = anything can access me

But apparently protected members works like "any instance of my own
class can access me (that is, also other instances than this!), but
for derived classes only the instance I belong to (this) can access
me". This instance limitation surprised me when I found out about it,
I don't get the idea behind it. Yet :)
When you change a protected method, would you like to know _exactly_ who the
change affects?

No, cause if Foo() would be used in other A::functions which are
accessible to descendents, I'd have the same situation anyway.
 
J

Jace/TBL

Andrey Tarasevich said:
This demonstrates the fact that protected member access control
specification is not really trying to implement a "per-instance" access
control. What it is trying to do though is to make sure that the
protected member you are trying to access from a method of class 'B'
does actually "belong" to class 'B' (as a subobject).

But why would that be necessary, all I want to be sure of is that bar
is an 'A', as that guarantees (by definition of A) that it can do
Foo(). It doesn't have to be a 'B', for all I care it can be any other
class derived from A.

Better yet: what if Foo() would not be virtual at all, and just a
protected member function of (and implemented in) A. Derived classes
could not override and reimplement this function. Then I still can't
call Foo() from within B on other A's than my own instance. Isn't that
"per-instance" access control?
 
A

Andrey Tarasevich

Jace/TBL said:
...

But why would that be necessary, all I want to be sure of is that bar
is an 'A', as that guarantees (by definition of A) that it can do
Foo(). It doesn't have to be a 'B', for all I care it can be any other
class derived from A.

In that case making 'Func' a protected member of 'A' is not an option.
Better yet: what if Foo() would not be virtual at all, and just a
protected member function of (and implemented in) A. Derived classes
could not override and reimplement this function. Then I still can't
call Foo() from within B on other A's than my own instance. Isn't that
"per-instance" access control?

No, it isn't. You _can_ call 'Foo' from other instances. Let me repost
the example from my previous message

void B::Foo(B *bar)
{
bar->Func();
}

...
B b1, b2;
b1.Foo(&b2); // OK

Note that parameter 'bar' can point to _any_ instance of class 'B' (or
class derived from 'B'). Method 'Foo' called for 'b1' can access 'Func'
for instance 'b2' without any problems. Obviously, this is not
"per-instance" access control. This is "per-class" access control.

The idea of protected access in C++ is that access is granted to the
derived class or can be "channeled", so to say, through the derived
class. But there's no way to circumvent the derived class and access the
protected member "directly" (which is what you are trying to do in your
original code).
 
J

Jace/TBL

Consider the following code:
(...)
C is a separate class. It knows nothing about B. So how can you call
C::Func, a protected member, in B ?

Because C is essentially also an A, and any A can do Func().

What if A had also a non-virtual Func2(A *that), also protected, which
calls that->Func(). Then in B::Foo I can do this->Func2(bar). I don't
see the problem in there, yet I would get the same situation as above.
In other words, bar need not be of
type B. If you change the code to

void Foo(B* bar)

it will work, because bar is guranteed to be of type B.

But I don't always want bar to be of type B, I want bar to be of any
type A - that is, A or derived from A. That's all I need to know that
it supports Func(). Otherwise there's no point in putting Foo as pure
virtual in A, I could just as well have made it a private function in
B.
 
A

Arijit

Because C is essentially also an A, and any A can do Func().

B is neither a friend of C, nor is derived from C. In fact, B and C are
completely independent, except that they share the same base class. Now recall
how protected works. If you use protected, it means that *only* that class
or its descendends will be allowed access. So if you allow B to access
C::Func() , it will violate this rule, because B is neither C not C's
descendant. C is A, and A can do Func() and so can C, but B cant' call
C::Func(), because it would violate the rules for protected. Virtual functions
can never lead to a situation where it can violate access rules.

It will be even more obvious with data members. Consider

class A {
protected:
int m;

// the rest
};

If in B you try bar->m, you will be accessing C's m. m may be in A, but
it is C's protected member. Why should B be allowed access to it ? If C went

class C: public A {
protected:
int n;

// etc.
};

you couldn't access n from B. Why can you access m ?

Think of it like this: If Y is X, Y can access X's protected members.
B is B, so B can access B's protected members. B is A, so B can access
A's protected members. B is not C, so B can't access C's protected members.

What if A had also a non-virtual Func2(A *that), also protected, which
calls that->Func(). Then in B::Foo I can do this->Func2(bar). I don't
see the problem in there, yet I would get the same situation as above.

Here A is calling Func() and through A*. What A is doing is calling its
own Func(), virtual function mechanism results in calling of a different
Func(). In B::Foo(A* bar), you are not calling through B*, you are calling
through A*. bar may be B*, maybe not. If it is not, you can't access it.
But I don't always want bar to be of type B, I want bar to be of any
type A - that is, A or derived from A. That's all I need to know that
it supports Func(). Otherwise there's no point in putting Foo as pure
virtual in A, I could just as well have made it a private function in
B.

But it will violate encapsulation. You just can't go about accessing other
classes protected functions just because they happen to share your base class.
If you could, it would violate encapsulation. Assume that you have a class
library, for example MFC. Now you wish to access the protected members of the
classes in the library. So you inherit a class from CObject, and begin to
access the protected methods of all the classes (those inherited from
CObject). Should such misuse be permitted ?

If you want to access Func(), you must make it public.

-Arijit
 
H

Howard

Jace/TBL said:
Suppose I have the following situation:

class A
{
protected:
virtual void Func() = 0;
};

class B : public A
{
protected:
virtual void Func();
void Foo( A *bar );
};

void B::Foo( A *bar )
{
bar->Func();
}

I get an error on bar->Func(), cause apparently I cannot call
protected functions on other instances than this. This surprised me, I
thought 'protected' would only limit the accessibility to the class
where it's defined and derived classes. But it also seems to allow
access *within this instance only*.

Is there a reasonable explanation for this behaviour of protected?

One other thing I haven't seen anyone metnion here yet:

You're calling bar->Func(), but bar is a pointer to an A object, and the
function Func() in A is pure virtual, and can NEVER be called! Even if you
could get by the problem of it being protected, if you DO pass a pointer to
an A object to the Foo function, then you'll get an exception for calling a
pure virtual function at run time!

You really need to have Foo accept a B* parameter, not an A* parameter,
because there's no guarantee otherwise that you won't get an A* passed to
you, which will fail.

-Howard
 
H

Howard

Jace/TBL said:
But why would that be necessary, all I want to be sure of is that bar
is an 'A', as that guarantees (by definition of A) that it can do
Foo(). It doesn't have to be a 'B', for all I care it can be any other
class derived from A.

How do you get that? There is no Foo() function in A. An A object cannot
call its Foo function if there isn't one. I'm wondering if you're confusing
the object whose Foo function is being called with the object that's getting
passed as a parameter to Foo. Those are two different objects. (They *may*
be the same object, but there's no requirement for it, anyway.) Perhaps you
didn't need a parameter at all, but merely wanted to call the Func()
function for the object whose Foo() is being called? In that case, just
have an empty parameter list for Foo(), and call Func() directly (without
the "bar->" specifier).

-Howard

"Vote early, and vote often!"
-anonymous (for obvious reasons)
 
A

Andrey Tarasevich

Howard said:
One other thing I haven't seen anyone metnion here yet:

You're calling bar->Func(), but bar is a pointer to an A object, and the
function Func() in A is pure virtual, and can NEVER be called! Even if you
could get by the problem of it being protected, if you DO pass a pointer to
an A object to the Foo function, then you'll get an exception for calling a
pure virtual function at run time!

You really need to have Foo accept a B* parameter, not an A* parameter,
because there's no guarantee otherwise that you won't get an A* passed to
you, which will fail.
...

Sorry, but this doesn't make sense. The problem that you describe
doesn't really exist. That's why no one mentioned it. Since class 'A' is
abstract, it is not possible to instantiate an 'A' object in the program
and, therefore, it is not possible to pass a pointer to an 'A' object to
'Foo'.

Just because the function is declared as pure virtual in class 'A'
doesn't mean that you shouldn't pass around pointers to 'A'. Moreover,
that's what one would normally do all the time. That's actually what
pure virtual functions and abstract classes are for.
 
H

Howard

D'OH!!!!


Never mind. I think I'll do something less important, and go vote now. :)

-Howard
 
A

Arijit

Howard said:
<snip>
One other thing I haven't seen anyone metnion here yet:

You're calling bar->Func(), but bar is a pointer to an A object, and the
function Func() in A is pure virtual, and can NEVER be called! Even if you
could get by the problem of it being protected, if you DO pass a pointer to
an A object to the Foo function, then you'll get an exception for calling a
pure virtual function at run time!

You really need to have Foo accept a B* parameter, not an A* parameter,
because there's no guarantee otherwise that you won't get an A* passed to
you, which will fail.

-Howard

Actually, the problem of calling a pure virtual function does not arise.
Assume Func() was public, then we could call bar->Func(). But when we
call A::Func(), the defined function in a derived class will be called.
The virtual function mechanism gurantees it. You cannot pass a pointer to
an actual A object because you cannot instantiate an A object. All you can
do is pass a pointer to a derived class of A which has defined Func(). You
can *never* call a pure virtual function, so the point of exception does not
arise.

-Arijit
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,898
Latest member
BlairH7607

Latest Threads

Top