Topic: Accessing protected member of base class through pointer


Author: allan_w@my-dejanews.com (Allan W)
Date: Wed, 23 Oct 2002 02:13:37 +0000 (UTC)
Raw View
dark@void.fslife.co.uk.removethisbit (Mark Boyd) wrote
> Is this legal? Should it be legal?

[Minor changes made to illustrate "problem" more succinctly]
> struct sheep {
>   int legs, eyes, heads;
>   sheep():legs(4),eyes(2),heads(1){}
> };
> class village {
>   protected:  sheep m_sheep;
> };
> void f(village &v) {
>
>   class hired_goons:private village {
>     public:
>     static sheep &steal_sheep(village &v) {
>       sheep village::*sheep_ptr = &hired_goons::m_sheep;  //EVIL
>       return v.*sheep_ptr;
>     }
>   };
>
>   sheep &s = hired_goons::steal_sheep(v);
>   s.legs = s.heads = s.eyes = 0; // Access protected subobject
> }
>
> As far as I know, the line marked with "EVIL" is allowed, but I can't
> think of a good reason why the standard should allow it.  Can anybody
> enlighten me?

Why shouldn't it be legal?

class "village" declares it's "sheep" member to be protected.
The word "protected" comes from the latin phrase for "sort of public,
sort of private." In C++ it means, "we don't just trust ANYBODY --
but we have to trust our derived classes."

class "hired_goons" is a derived class, and must be trusted to handle
the sheep with care. Member function "steal_sheep" voilates that
trust.

To solve this "problem," you have a lot of options:

  1. Find the programmer who wrote steal_sheep, and shoot him (or at
     least fire him). Best to do this at a code walk-through, so
     everyone knows why he was fired. Helps to avoid similar problems
     in the future.

  2. Change hired_goons so it no longer derives from village.

  3. Change the keyword "protected" in village to "private."

  4. Accept the fact that m_sheep isn't so protected after all.
     (After all, there are other ways to get the same address;
     if you have no coding standards, you simply can't prevent it!)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: hennebry@web.cs.ndsu.nodak.edu (Michael J. Hennebry)
Date: Wed, 23 Oct 2002 17:39:39 +0000 (UTC)
Raw View
I can't even parse the line labeled EVIL, but I get the idea.
It's illegal.
Using a derived class to access a protected member of its base class
is only allowed when the base class object is part of an object of the
derived class.

class henhouse {
protected:
   int hens;
} ;

class fox : henhouse {
public:
static int *toHens(henhouse &hh) { return &hh.hens; }    // ILLEGAL
} ;

The line labeled ILLEGAL is illegal because hh is only a reference to
a henhouse object, not a fox object.  Evilness may be accomplished
with an inappropriate cast on hh.  My recollection is that a compiler,
if it notices, may reject such a cast.  There isn't much point in
making the compiler notice.  Access rules are designed to prevent
accidents, not on purposes.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: allan_w@my-dejanews.com (Allan W)
Date: Fri, 25 Oct 2002 02:15:12 +0000 (UTC)
Raw View
hennebry@web.cs.ndsu.nodak.edu (Michael J. Hennebry) wrote
> I can't even parse the line labeled EVIL, but I get the idea.
> It's illegal.

The OP's point was that it SHOULD be illegal, but it isn't.

> Using a derived class to access a protected member of its base class
> is only allowed when the base class object is part of an object of the
> derived class.
>
> class henhouse {
> protected:
>    int hens;
> } ;
>
> class fox : henhouse {
> public:
> static int *toHens(henhouse &hh) { return &hh.hens; }    // ILLEGAL
> } ;
>
> The line labeled ILLEGAL is illegal because hh is only a reference to
> a henhouse object, not a fox object.

But that's not what the OP did. I'll remove the pointer-to-member
operator that you seem to find tough to parse. What the OP did is
equivalent to this:

    void foo(henhouse &h) {

        std::cout << "Henhouse has ";

        // Illegal -- but see workaround below
        //std::cout << h.hens;

        // We can't access h.hens, but a derived class can.
        struct fox : private henhouse {
            fox(henhouse&h) : henhouse(h) {};
            int &hens() { return henhouse::hens; } // Immoral but legal
        };
        fox f(h); // Construct the derived class, and then
        // Access the element
        std::cout << f.hens();

        std::cout << " hens!\n";
    }

In the OP's code and in this one, the use of "protected" instead of
"private" provides a loophole. Then some function creates a class for
no other reason than to exploit that loophole -- accessing data that
is conceptually private, using a derived class instead of any sort
of cast.

My point is that "protected" means "mostly private but with exceptions."
If you have coders that like to exploit those exceptions without a
really excellent reason, then either stop using protected, fire those
coders, or live with it -- the compiler is doing exactly what you asked
for.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: brangdon@cix.co.uk (Dave Harris)
Date: Fri, 25 Oct 2002 21:29:54 +0000 (UTC)
Raw View
allan_w@my-dejanews.com (Allan W) wrote (abridged):
> I'll remove the pointer-to-member operator that you seem to
> find tough to parse [...]
>
>         struct fox : private henhouse {
>             fox(henhouse&h) : henhouse(h) {};
>             int &hens() { return henhouse::hens; } // Immoral but legal
>         };
>         fox f(h); // Construct the derived class, and then
>         std::cout << f.hens();

Presumably the pointer-to-member is necessary to make it access the
original member. Your version seems to access a copy. In other words:

    std::cin >> f.hens();

would not affect h.

  Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
      brangdon@cix.co.uk      |   And close your eyes with holy dread,
                              |  For he on honey dew hath fed
 http://www.bhresearch.co.uk/ |   And drunk the milk of Paradise."

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dark@void.fslife.co.uk.removethisbit (Mark Boyd)
Date: Sat, 26 Oct 2002 22:50:03 +0000 (UTC)
Raw View
Allan W wrote:

> dark@void.fslife.co.uk.removethisbit (Mark Boyd) wrote
>> Is this legal? Should it be legal?
>
> [Minor changes made to illustrate "problem" more succinctly]
>> struct sheep {
>>   int legs, eyes, heads;
>>   sheep():legs(4),eyes(2),heads(1){}
>> };
>> class village {
>>   protected:  sheep m_sheep;
>> };
>> void f(village &v) {
>>
>>   class hired_goons:private village {
>>     public:
>>     static sheep &steal_sheep(village &v) {
>>       sheep village::*sheep_ptr = &hired_goons::m_sheep;  //EVIL
>>       return v.*sheep_ptr;
>>     }
>>   };
>>
>>   sheep &s = hired_goons::steal_sheep(v);
>>   s.legs = s.heads = s.eyes = 0; // Access protected subobject
>> }
>>
>> As far as I know, the line marked with "EVIL" is allowed, but I
>> can't
>> think of a good reason why the standard should allow it.  Can
>> anybody enlighten me?
>
> Why shouldn't it be legal?
>
> class "village" declares it's "sheep" member to be protected.
> The word "protected" comes from the latin phrase for "sort of
> public, sort of private." In C++ it means, "we don't just trust
> ANYBODY -- but we have to trust our derived classes."
>
> class "hired_goons" is a derived class, and must be trusted to
> handle the sheep with care. Member function "steal_sheep" voilates
> that trust.

I don't think derived classes should be trusted with the sheep.
Notice that the most obvious try doesn't work:

   class incompetent_goons:private village {
     public:
      static sheep &steal_sheep(village &v) {
         return v.m_sheep; //Illegal:  m_sheep is protected!
       }
     };

Without the "EVIL" pointer trick, a class derived from village can
only access the sheep inside other objects of that derived class.  It
can't mess up sheep in a village which is not a derived class.

This is good, because there might be several different classes
derived from village which use their sheep in different ways, and
don't know the proper way to handle other classes' sheep.


>
> To solve this "problem," you have a lot of options:
>
>   1. Find the programmer who wrote steal_sheep, and shoot him (or at
>      least fire him). Best to do this at a code walk-through, so
>      everyone knows why he was fired. Helps to avoid similar
>      problems in the future.
>
Shoot myself?  This isn't ideal; it solves the problem for me but not
for anybody else.


>   2. Change hired_goons so it no longer derives from village.
>
This is essentially saying "Don't do that!"  I agree, but it would be
nice if the standard said the same thing (i.e. didn't allow it).


>   3. Change the keyword "protected" in village to "private."

I'm not sure what you're getting at here.  Sometimes things should be
protected.

>
>   4. Accept the fact that m_sheep isn't so protected after all.
>      (After all, there are other ways to get the same address;
>      if you have no coding standards, you simply can't prevent it!)

Are there really other ways?  I can think of a few things which work
on my PC (and probably most sane implementations), but officially
don't work, because they cause undefined behaviour.

-Mark

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: hennebry@web.cs.ndsu.nodak.edu (Michael J. Hennebry)
Date: Sat, 26 Oct 2002 22:56:40 +0000 (UTC)
Raw View
allan_w@my-dejanews.com (Allan W) wrote in message news:<7f2735a5.0210241420.5143b5db@posting.google.com>...
> hennebry@web.cs.ndsu.nodak.edu (Michael J. Hennebry) wrote
> > I can't even parse the line labeled EVIL, but I get the idea.
> > It's illegal.
>
> The OP's point was that it SHOULD be illegal, but it isn't.

Mark Boyd (dark@void.fslife.co.uk.removethisbit)
wrote and I can parse better now:

>  class hired_goons:private village
>  {
>    public:
>    static sheep &steal_sheep(village &v)
>    {
>      sheep village::*sheep_ptr = &hired_goons::m_sheep;  //EVIL
>
>      return v.*sheep_ptr;
>    }
>  };

The line labeled EVIL is an illegal conversion.
For pointers to members, derived to base conversions are not allowed.
Allowing such conversions would allow pointers to non-existent members.

> > Using a derived class to access a protected member of its base class
> > is only allowed when the base class object is part of an object of the
> > derived class.
> >
> > class henhouse {
> > protected:
> >    int hens;
> > } ;
> >
> > class fox : henhouse {
> > public:
> > static int *toHens(henhouse &hh) { return &hh.hens; }    // ILLEGAL
> > } ;
> >
> > The line labeled ILLEGAL is illegal because hh is only a reference to
> > a henhouse object, not a fox object.
>
> But that's not what the OP did. I'll remove the pointer-to-member
> operator that you seem to find tough to parse. What the OP did is
> equivalent to this:
>
>     void foo(henhouse &h) {
>
>         std::cout << "Henhouse has ";
>
>         // Illegal -- but see workaround below
>         //std::cout << h.hens;
>
>         // We can't access h.hens, but a derived class can.
>         struct fox : private henhouse {
>             fox(henhouse&h) : henhouse(h) {};
>             int &hens() { return henhouse::hens; } // Immoral but legal
                                                     // not immoral
>         };
>         fox f(h); // Construct the derived class, and then
>         // Access the element

f has only a copy of h, not the orignal.

>         std::cout << f.hens();

f.hens()=0 would not empty h, just the henhouse that is part of f.

>
>         std::cout << " hens!\n";
>     }
>
> In the OP's code and in this one, the use of "protected" instead of
> "private" provides a loophole. Then some function creates a class for
> no other reason than to exploit that loophole -- accessing data that
> is conceptually private, using a derived class instead of any sort
> of cast.

I sometimes do that sort of thing when debugging, mostly to print
protected members.  In that case, I do a cast from pointer
to base to pointer to derived.  After eliminating the derived class,
the compiler will gleefully tell me about any related statements
that I forget to get rid of.

For complicated data structures, copying might not be such
a good idea.  For that matter, the copy constructor, if any,
might not even be accessable.

I've read the suggestion that protected data members are a bad idea.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dark@void.fslife.co.uk.removethisbit (Mark Boyd)
Date: Mon, 21 Oct 2002 17:15:57 +0000 (UTC)
Raw View
Is this legal?

Should it be legal?


struct sheep
{
  int legs;
  int eyes;
  int heads;

  sheep():legs(4),eyes(2),heads(1){}
};

class village
{
protected:
     sheep m_sheep;
};

void f(village &v)
{

  class hired_goons:private village
  {
    public:
    static sheep &steal_sheep(village &v)
    {
      sheep village::*sheep_ptr = &hired_goons::m_sheep;  //EVIL

      return v.*sheep_ptr;
    }
  };

  sheep &s = hired_goons::steal_sheep(v);

  //protected?  Hah!
  s.legs = 0;
  s.heads = 100;
  s.eyes = 1;

}



This problem came up in an (old) thread in c.l.c++ - sorry if this
link wraps.

http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&frame=right&th=2cb4c0af2c
b56347&seekm=solY8.532749%24cQ3.47842%40sccrnsc01#link1

As far as I know, the line marked with "EVIL" is allowed, but I can't
think of a good reason why the standard should allow it.  Can anybody
enlighten me?


-Mark

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]