Thread: Access to members of protected base class

  1. #1
    Registered User
    Join Date
    Jun 2008
    Location
    Somewhere in Europe
    Posts
    99

    Access to members of protected base class

    Hello all.

    This won't compile:

    Code:
    #include <iostream>
    
    using namespace std;
    
    class B
    {
        public:
        int a;
    };
    
    class D: protected B
    {
    };
    
    class E: public D
    {
        void f(D* pD)
        {
            B* pB = pD;
            pD->a = 7;
        }
    };
    
    
    int main()
    {
    }
    the problem being that B::a is inaccessible in the line pD->a = 7;.

    Can anyone explain why this is the case? Is it because member functions of E can access protected (and public) members of the B and D parts of E objects only, and the compiler can't know whether pD points to an E object (as opposed to an object of, say, class F: D{}; )?

    Thanks.

  2. #2
    Registered User
    Join Date
    Mar 2007
    Posts
    416
    I believe it's because class B is protected, and you're trying to access something like it is a public member of E. Really it's a public member of class B, and B is protected in D, and D is public in E, but E is trying to get a protected public function... if that makes sense.

    I could be wrong.

  3. #3
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    First, simplify your test case. Class B is an unnecessary distraction.
    Code:
    class D
    {
    protected:
        int a;
    };
    
    class E: public D
    {
        void f(D* pD)
        {
            pD->a = 7;
        }
    };
    This exhibits the same problem. Why? Because the protection model says that you can only access those protected members that you actually inherited. So you can access this->a, but not pD->a, because pD might point to any D-derived class (E, F, G, whatever), not just an E.

    The rule basically says, "A member with protected access can only be accessed through a reference (dereferenced pointer, object name, real reference) whose static type is of the current class, or a class derived from it."
    (Note that a friend of a derived class cannot access inherited protected members of that class; or at least GCC doesn't allow it.)
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  4. #4
    Registered User
    Join Date
    Jun 2008
    Location
    Somewhere in Europe
    Posts
    99
    I guess Bjarne Stroustrup says the same thing when he states that "a derived class can access a base class' protected members only for objects of its own type", although in the example he gives of what not to do, he tries to access the protected member via a pointer to F (or G, or whatever) rather than a pointer to D.

    Re friends, this seems to compile OK with the GNU GCC compiler on my machine:

    Code:
    class D
    {
    protected:
        int a;
    };
    
    class E: public D
    {
        friend void f(E*);
    };
    
    void f(E* pE)
        {
            pE->a = 7;
        }
    
    int main()
    {
        E e1;
        f(&e1);
        return 0;
    }

  5. #5
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Strange, pretty much the same code didn't compile for me with GCC. Ah well, maybe I had a mistake in my test.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  6. #6
    Registered User
    Join Date
    Jun 2008
    Location
    Somewhere in Europe
    Posts
    99
    Incidentally, according to Stroustrup, both the statements in the function f in the following should give compilation errors (my code is slightly shortened w.r.t. his, but I am sure I have not altered anything important):

    Code:
    class X
    {
    public:
        int a;
    };
    
    class Y2: protected X{};
    
    class Z2: public Y2 {void f(Y2*);};
    
    void Z2::f(Y2* py2)
    {
        X* px = py2;
        py2->a = 7;
    }
    
    
    int main()
    {
        return 0;
    }
    My compiler has no problem with "X* px = py2;". S's comment is "error: X is a protected base of Y2, and Z2 is derived from Y2, but we dont know that py2 is a Z2 or how Y2::X is used in a non-Z2 object."

    Is it possible he has made mistake here, or is the compiler wrong?

  7. #7
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Interesting question. Stroustrup is definitely right in the sense of "this is how it should be". But I'd have to read the standard very carefully to find out if he's right in the sense of "this is what the standard language says".
    Consider why this shouldn't be allowed.
    Code:
    class Base
    {
    public:
      int a;
    };
    class Middle : protected Base
    {
    };
    class Final1 : public Middle
    {
    };
    class Final2 : public Middle
    {
      void f(Middle* m) {
        m->a = 0; // Fordbidden, or Final2 would modify Final1's protected member.
        Base *b = m; // This must therefore also be forbidden,
        b->a = 0; // because the compiler cannot possible catch this.
      }
    };
    int main()
    {
      Final1 f1;
      Final2 f2;
      f2.f(&f1);
    }
    To keep the special rule for protected from being trivially subverted, the cast to the base must be forbidden.
    Checking the standard shows that this is indeed the case. 11.2p4 says that a base class is accessible (i.e. you can cast to it) if an invented public member of that class is accessible. We don't need to invent anything - the compiler clearly agrees that the actual public member 'a' of that class is not accessible. Thus, it should not allow the cast.

    However, this is pretty complex stuff. I've seen one implementation of base access checking, and the code is very complicated. I don't blame GCC for not implementing this detail.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  8. #8
    Registered User
    Join Date
    Jun 2008
    Location
    Somewhere in Europe
    Posts
    99
    I hope this isn't a stupid question, but why can't the compiler catch

    b->a = 0; ?

    Surely b could be a pointer to Final1 just as easily as m could?

  9. #9
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    'a' is public within Base. Thus, access to 'a' through a pointer to Base is perfectly legal. To catch the invalid access, the compiler would have to trace the history of the pointer to the assignment from the 'Middle' pointer. Tracing the history of a value is hard at best.
    It's also useless, because there should never be a Base pointer to an object whose Base part is inaccessible, because the cast shouldn't be allowed in the first place.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  10. #10
    Registered User
    Join Date
    Jun 2008
    Location
    Somewhere in Europe
    Posts
    99
    I see that 'a' is public within Base. But Base is a protected base of Middle, so 'a' is a protected member of Middle. And Final2 is derived from Middle.

    Surely then accessing 'a' via a pointer to Base from within Final2 contravenes the principle that a "derived class [Final2] can access a base class' [Middle] protected members ['a'] only for objects of its own type [Final2]" and "a member with protected access can only be accessed through a reference (dereferenced pointer, object name, real reference) whose static type is of the current class, or a class derived from it."

  11. #11
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    But the Base pointer might point to anything.
    Code:
    class Anything : public Base
    {
    };
    If that Base pointer points to an Anything, accessing the 'a' through it is perfectly valid. Therefore, the compiler cannot verify if an access to 'a' through a Base pointer is valid or not. This further means that if you let the programmer get a Base pointer, you've already lost. You have to stop him from getting the Base pointer in the first place, which means forbidding the conversion from Middle to Base, unless you're in the context of the Middle class.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  12. #12
    Registered User
    Join Date
    Jun 2008
    Location
    Somewhere in Europe
    Posts
    99
    So you have the following situation:

    Code:
    class A
    {
        public: int a;
    };
    
    class B: public A{};
    
    class C: public B{};
    
    class D: protected C{};
    
    class E: public D{};
    
    class F: public E{};
    
    class G: public F
    {
        void f(A* pa, B* pb, C* pc, D* pd, E* pe, F* pf, G* pg)
        {
            pa->a = 0;// OK
            pb->a = 0;// OK
            pc->a = 0;// OK
            pd->a = 0;// ill-fomed
            pe->a = 0;// ill-formed
            pf->a = 0;// ill-formed
            pg->a = 0;/OK
        }
    };
    
    int main()
    {
        return 0;
    }
    with casts from pf, pe and pc to pa from within G strictly speaking also being forbidden?

  13. #13
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Yes, sounds right.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  14. #14
    Registered User
    Join Date
    Jun 2008
    Location
    Somewhere in Europe
    Posts
    99
    Thanks.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. derived class can not access base class protected member?
    By George2 in forum C++ Programming
    Replies: 2
    Last Post: 10-21-2007, 06:32 PM
  2. Defining derivated class problem
    By mikahell in forum C++ Programming
    Replies: 9
    Last Post: 08-22-2007, 02:46 PM
  3. Base class initialization
    By VirtualAce in forum C++ Programming
    Replies: 4
    Last Post: 01-11-2004, 04:52 AM
  4. Replies: 4
    Last Post: 12-29-2002, 12:29 AM
  5. Virtual Base Class & Constructor :: C++
    By kuphryn in forum C++ Programming
    Replies: 2
    Last Post: 09-13-2002, 03:14 PM