A static function in a derived class can't access a protected data
member from the base class, although class access is public:
class A {
protected:
static void f(A* instance) {
++(instance->d_);
}
int d_;
};
class B : public A {
protected:
void g() {
--d_; // sanity check: accessing protected member from non-
static function is fine
}
static void f(A* instance) {
--(instance->d_); // error: 'A::d_' : cannot access
protected member declared in class 'A'
}
};
However, if the instance pointer is changed to point to the derived
class, everything is fine:
class B : public A {
protected:
static void f(B* instance) { // points to an instance of B now
--(instance->d_); // fine
}
};
Just out of curiosity, what's going on here? I got a hunch that
accessing A->d_ from B::f() is like accessing d_ from outside the
class, which is not allowed because d_ is protected. But why would A*
be considered a pointer to an arbitrary class, when the compiler knows
that B is derived from A and has access to its protected members?
Could someone clear that up, please?
Thanks, Andreas.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
The part you're overlooking is that protected access is limited inside
a derived class to only grant access to other objects of the same
(derived) type. If you were allowed to do what you are trying to do,
then if you had multiple classes sharing a common base, they would be
allowed to edit each other's protected data, which violates the
promise that only the derived class can access.
Given an arbitrary pointer to the base type, there is no way to know
at compile time the dynamic type of the "pointed-to" object, and so
there is no way to know whether access should be granted, so it's not.
For a concrete example:
class base {
protected:
int data;
};
class derived1 : public base {};
class derived2 : public base {
public:
void f(base * b) {
b->data = 999; // not allowed
}
};
int main() {
derived1 d1;
derived2 d2;
d2.f(&d1);
}
If derived2::f() were allowed to edit b->data directly, then it could
access the data in other derived types. Objects of type derived1 are
only allowed access to derived1::data, and objects of type derived2
are only allowed access to derived2::data.
--
Chris
You mean, a particular static function.
The fact that it's static is irrelevant here.
> class A {
> protected:
> static void f(A* instance) {
> ++(instance->d_);
> }
> int d_;
> };
>
> class B : public A {
> protected:
> void g() {
> --d_; // sanity check: accessing protected member from non-
> // static function is fine
> }
> static void f(A* instance) {
> --(instance->d_); // error: 'A::d_' : cannot access
> // protected member declared in class 'A'
> }
> };
It has nothing to do with static versus non-static, but all to do with ensuring
the stated access level, 'protected'.
To make the non-static member function B::g comparable to B::f, you would have
to change B::g to be a copy of B::f without the 'static' keyword. Then you'd get
the same error. As it is you're comparing apples with oranges.
> However, if the instance pointer is changed to point to the derived
> class, everything is fine:
>
> class B : public A {
> protected:
> static void f(B* instance) { // points to an instance of B now
> --(instance->d_); // fine
> }
> };
>
> Just out of curiosity, what's going on here?
Here you've changed B::f to essentially be a copy of B::g, just with 'static'
added, and the situation is the same, no error.
> I got a hunch that
> accessing A->d_ from B::f() is like accessing d_ from outside the
> class, which is not allowed because d_ is protected.
No, the last example illustrates that it matters very much that it's a member
function. If you'd made the last example's B::f a free-standing function it
wouldn't have compiled. The 'static' or not does not matter, but class
membership does.
> But why would A*
> be considered a pointer to an arbitrary class, when the compiler knows
> that B is derived from A and has access to its protected members?
> Could someone clear that up, please?
There is a class A, very much like your class A, with a protected data member.
There's also a class Target derived from A, written by someone else. Me, the
novice hacker, I'm very much interested in accessing the d_ member of a Target
instance directly (which may break assumptions of class Target, but I'm doing
this to shave off a nano-second), and to do that I ingeniously derive from A:
<code>
#include <iostream>
class A
{
protected:
int d_;
public:
A(): d_(42) {}
int d() const { return d_; }
};
class Target: public A {};
class TargetHack: public A
{
public:
static void setD( A& a ) { a.d_ = 666; }
};
int main()
{
Target o;
TargetHack::setD( o );
std::cout << o.d() << std::endl;
}
</code>
This does not compile. And the reason it doesn't compile is that the C++ access
rules are designed to not let me hack into a class that easily. There is of
course nothing preventing me from using casts or preprocessor stuff or other
such things to gain entrance, perhaps with some damage, but the door is not
open: I can't get in there by *accident*, the 'protected' *does* have an effect.
Well, except for...
Type system loophole. If you use member pointers you *can* gain entrance by
accident. In the simple, clear example code below this would not be by accident,
but in somewhat more complex code the same could conceivably happen by accident.
So this is worth keeping in mind: member pointers are not type safe. They're
very very un-typesafe. Think of them as simple offsets, where it's you who are
telling the compiler what actual type that object is, i.e., that copying a
member pointer essentially involves an *implicit cast* (to be pedantic, a cast
is a syntactic thing whereas here it's all about type conversion, but), which
cast in effect transfers the accessibility up to a class where it shouldn't be.
Put another way, while macros don't respect scopes, member pointers don't
respect accessibility.
So member pointers are sort of at the level of macros, but with the problem that
they might seem to be type safe, which they're not.
<code>
#include <iostream>
class A
{
protected:
int d_;
public:
A(): d_(42) {}
int d() const { return d_; }
};
class Target: public A {};
class TargetHack: public A
{
public:
static void setD( A& a )
{
int A::*p = &TargetHack::d_;
a.*p = 666;
}
};
int main()
{
Target o;
TargetHack::setD( o );
std::cout << o.d() << std::endl;
}
</code>
Compared to the H-bomb effect of implicit conversion from array to pointer this
type system hole is almost like a little firecracker, just fun, amusement for
tired C++ programmers. But, if a firecracker goes off at the wrong time and
place, it can do very much damage. So, it's a generally a good idea to avoid
direct use of member pointers, just as with avoiding direct use of raw pointers
in general, and avoiding goto in general, and other direct use of lowest levels.
I guess one could spark off a little heated debate by asserting that hey, it's
not the fault of member pointers, it's the use of 'protected' data!
So, herewith :o), but since my articles generally have a much higher lag time
for acceptance than others', I probably won't be able to participate.
Cheers & hth.,
- Alf
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?