In C++ you can access a private member in a different object of the same
type in a member function. So:
class A {
public:
A () : x (0) {}
A (const A& a) : x (a.x) {} // <-- accessing private x in a
private:
int x;
};
But I cannot access a protected member in a different object of a base
type. So:
class A {
protected:
int x;
};
class B : public A {
public:
void f (A& a) { a.x = 0; } // <-- error: cannot acces protected
// member A::x
};
To me, that's very unlogical! I see no reason why this is wrong:
- Type safety is type-based, so I would expect that any member
function of type B can access any protected member of any
object of a base type of B. Basically the same behaviour as if
both object are of the same type (first example).
- The compiler has all needed information available, because
you cannot derive from a partial type.
It does work with static members however:
class A {
protected:
int x;
static void set_x (A& a, int xx) { a.x = xx; }
};
class B : public A {
public:
void f (A& a) { set_x (a, 0); } // <-- ok
};
I tested it with VC6, VC8, gcc 3.4 and gcc 4, all issue the same error.
So what am I'm missing? Is it by design, or is it a bug? And if it is by
design, what's the logic behind it?
Thanks, Luke
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
You could say that a class always is declared as a friend of itself.
And friendship is not inherited in C++, not in the real world ether :)
. That's why your code above doesn't compile. The reason for this
"compiler generated friend class of itself", I guess, is that if the
copy constructor (or asignment operator) should do its job, then it
have to have access to the data of the object it will copy from. If it
didn't, you had to make access functions to all data if you wanted a
copy constructor, and what should the compiler generated copy
constructor do? So for me, to have every class to be friend of itself,
is logical.
But why is not friendship inherited? if so what would be the point of
having protected members? Protected members should only be accessed by
objects of the derived class or objects of the class declaring the
member. If every class was friend with inherited classes then the
private member would act protected to the derived class but private to
the outside world. You would never have real private members.
> It does work with static members however:
>
> class A {
> protected:
> int x;
> static void set_x (A& a, int xx) { a.x = xx; }
> };
>
> class B : public A {
> public:
> void f (A& a) { set_x (a, 0); } // <-- ok
> };
>
set_x(..) is protected in A, so B should inherit this function, and
here you call set_x(..) class B. So this should be OK. The logic is of
couars, that if set_x is protected, all derived class can accsess this
functin by calline set_x(..) :)
> I tested it with VC6, VC8, gcc 3.4 and gcc 4, all issue the same error.
> So what am I'm missing? Is it by design, or is it a bug? And if it is by
> design, what's the logic behind it?
I know my explanations is not allways perfect, but I hope this helps.
-Thomas
Yes.
> But I cannot access a protected member in a different object of a base
> type.
Yes, exactly.
> To me, that's very unlogical! I see no reason why this is wrong:
A derived class can access the protected interface of its base class.
If it could access the protected interface of ANY object using the same
base class, it would expose data to unrelated objects.
For a contrived example, this code shows why it is NOT a good idea to
do what you are wishing. It is a re-enactment of Watergate, C++ style:
#include "secret.hpp"
#include <iostream>
class headquarters
{
protected:
secret_files keep_out_;
};
class democrat_hq : public headquarters { /*...*/ };
class republican_hq : public headquarters
{
public:
void snoop(headquarters const & hq)
{
std::cout << hq.keep_out_ << std::endl;
}
};
int main()
{
democrat_hq dhq;
republican_hq rhq;
rhq.snoop(dhq); // UH OH!
}
Fortunately, it doesn't compile. For snoop() to work, it would have to
take a reference to a republican_hq as its argument. But then you
would not be able to pass in a democrat_hq. (Though the current
administration would probably just use a reinterpret_cast to get around
that!)
The only time an object can access the protected members of another
object's base class is if the other object is the same type (or
inherits from the same type) as itself. If it is passed in as a base,
then the type system cannot be sure that the underlying object is the
right type, and so access is denied.
--
Chris
> You could say that a class always is declared as a friend of itself.
Isn't friendship a type relation, and not an object relation? This
implies that every instance is in fact of a different type, or, that
there are two kinds of friendship: type-friendship and
instance-friendship.
> And friendship is not inherited in C++, not in the real world ether :)
> . That's why your code above doesn't compile. The reason for this
> "compiler generated friend class of itself", I guess, is that if the
> copy constructor (or asignment operator) should do its job, then it
> have to have access to the data of the object it will copy from. If it
> didn't, you had to make access functions to all data if you wanted a
> copy constructor, and what should the compiler generated copy
> constructor do? So for me, to have every class to be friend of itself,
> is logical.
>
> But why is not friendship inherited? if so what would be the point of
> having protected members? Protected members should only be accessed by
> objects of the derived class or objects of the class declaring the
> member. If every class was friend with inherited classes then the
> private member would act protected to the derived class but private to
> the outside world. You would never have real private members.
I agree that this would destroy encapsulation.
But inheriting friendship is not needed for this.
- The compiler knows we are in the context of a member function.
- The compiler knows we're accessing an object as a base type.
- It already works this way for privates and statics.
>
> > It does work with static members however:
> >
> > class A {
> > protected:
> > int x;
> > static void set_x (A& a, int xx) { a.x = xx; }
> > };
> >
> > class B : public A {
> > public:
> > void f (A& a) { set_x (a, 0); } // <-- ok
> > };
> >
>
> set_x(..) is protected in A, so B should inherit this function, and
> here you call set_x(..) class B. So this should be OK. The logic is of
> couars, that if set_x is protected, all derived class can accsess this
> functin by calline set_x(..) :)
>
Why is the same not true for a non-static member function? That is, if
I declare set_x as non-static member in A, I cannot call it on an
instance other then 'this':
void B::f(A& a) { set_x (a, 0); } // <-- ok
void B::f(A& a) { a.set_x (0); } // <-- error
I'm trying to think of an example where allowing access on protected
members in a member function via a pointer that is not 'this' causes
havoc (break encapsulation), but I can't think of any.
> I know my explanations is not allways perfect, but I hope this helps.
>
> -Thomas
>
Maybe I should rephrase my question: why is 'this' special?
In the sense that in a member function of a type derived from T, I can
only access protected base members via 'this', but not via any other
pointer T. Is this the same in all OO languages? And is there a deep
reason for it, or is it just 'convenience' for compiler builders.
To my mind, this is for very pragmatic reasons: one class is one unit
of responsibility. The programmer who wrote or who is maintaining the
class can mess up with the class internals, it's her duty to get it
right; the programmer responsible of any derived class is just not
allowed to do this, in order to not mess up the work done by the first
programmer.
Cheers
Paavo