Can a friend class object access base class private members on a derived class object?

后端 未结 5 1843
佛祖请我去吃肉
佛祖请我去吃肉 2021-02-19 21:39

I\'m surprised that the code below compiles.

It seems that a class befriended to the (publicly inherited) base class can access a member of the base class provided an in

相关标签:
5条回答
  • 2021-02-19 22:12

    Object of class D is composed of 2 separate parts :

    part containing members of B 
    part containing members of D
    

    That why the concept of object slicing works when we do:

    D objD;
    B objB = objD;
    

    Now we can access from inside object of class D, the part containing members of B via objB. Compiler remembers or can distinguish between the two parts inside class D. So compiler know what is being accessed via what.

    The statement friend class F; inside class B simply tells that member functions of class F can accesses the private, protected and public members of class B. That is, for member functions of class F all the members of class B are public.

    Actually, inside every class there are three sections w.r.t accessibility:

    public
    protected
    private 
    

    So when we declare some class B:

    class B
    {
        public:
            int a;
        protected:
            int b;
        public:
            int c;
    };
    

    then following 3 sections get created inside class B as shown above.

    Now when we declare some class F to be a friend of class B:

    class B
    {
        friend class F;
        private:
            int a;
        protected:
            int b;
        public:
            int c;            
    };
    

    then the compiler creates the sections as follows:

    class B
    {
        friend class F;
        private:
            int a;
        protected:
            int b;
        public:
            int c;
            //int a;  only for member functions of class F
            //int b;  only for member functions of class F             
    };
    

    Note that int a; and int b; are now public for member functions of class F.

    Now when class D is derived publicly from class B then the public section of class B becomes public section of class D. Similary, the protected section of class B becomes protected section of class D. Therefore, the public section part of class B can be accessed via object of class D. And since B::a; and B::b; are in public section for members functions of class F, therefore B::a and B::b can be accessed via object of class D. Also note that although after derivation int a; and int b; become members of class D, still compiler is able to distinguish them and considers them a part of class B.

    Now when class D is derived privately from class B then the public section of class B becomes private section of class D. Similary, the protected section of class B becomes protected section of class D. Therefore, now the public section part inside of class B cannot be accessed via object of class D. Recall that in class B, B::a; and B::b; are originally in public section for members functions of class F but after private derivation, the members of class B i.e B::a and B::b are now in private section of class D. Therefore, B::a and B::b cannot be accessed via object of class D. Also note that although after derivation int a; and int b; become members of class D, still compiler is able to distinguish them and considers them a part of class B. After derivation the accessibility and rules of some members of class B have changed.

    Since this question somewhat relates to effect of public, protected and private derivation, therefore for completeness please see: Why can a derived class not access a protected member of its base class through a pointer to base?

    0 讨论(0)
  • 2021-02-19 22:14
    1. It seems that somehow friendship is inherited and a friend class can access a member of the derived class.
      In short, how is d.b_var valid within F::func(D& d)?

    d.b_var might be misleading. To be more precise (another way to see it), b_var is not (direct) member of derived class D. Instead, the object of D contains a subobject of base class B, which has member b_var, and could be accessed by the friend F. (As we can also write d.b_var as d.B::b_var.)

    $10/3 Derived classes [class.derived]:

    The base-specifier-list specifies the type of the base class subobjects contained in an object of the derived class type. [ Example:

    struct Base {
      int a, b, c;
    };
    
    struct Derived : Base {
      int b;
    };
    
    struct Derived2 : Derived {
      int c;
    };
    

    Here, an object of class Derived2 will have a subobject of class Derived which in turn will have a subobject of class Base. — end example ]

    And

    1. If the inheritance is changed to private then compilation fails.

    Because

    class B {
        int b_var;
        friend class F;
    };
    
    class D: private B {
        int d_var;
    };
    
    class F{
    public:
        void func(D &d) {
            d.b_var = 5;  // Fail. Can't access subobject of B
            d.d_var = 5;  // Fail. Can't access member of D
        }
    };
    

    Then

    class B {
        int b_var;
    };
    
    class D: private B {
        friend class F;
        int d_var;
    };
    
    class F{
    public:
        void func(D &d) {
            d.b_var = 5;  // Fail. Can't access b_var of subobject of B
            d.d_var = 5;  // Fine.
        }
    };
    

    Note that in last case, even F is friend of class D, it could access all the private/protected members of D, but not including members in subobject B, because they're not (direct) members of class D.

    0 讨论(0)
  • 2021-02-19 22:24

    D is a B when public inheritance is used. So accessing b_var is still perfectly legal.
    You would get an error, however, if you attempt to access d_var, since the friendship itself is not inherited, as you seem to be aware.

    Inheritance always makes all members of the base be members of the derived. Access specifiers only affect where an identifier is visible. That's why accessing a private member illegally produces a different error to accessing an identifier that doesn't exist.

    0 讨论(0)
  • 2021-02-19 22:24

    While there already are good answers I think some images would help here a bit too.

    This is an abstraction of your B class. F has access to all its members.

    When you now instantiate a D object it looks like this

    It still is a B object but also a D object. It extends B so to speak. F can still access the part from B because it's still there but not from D.

    Please note that these abstractions do not really display the layout in memory and explain overriding etc. But they are just for the sake of this question.

    0 讨论(0)
  • 2021-02-19 22:28

    You write:

    It seems that somehow friendship is inherited and a friend class can access a member of the derived class.

    But it rather should be:

    It seems that a class befriended to the (publicly inherited) base class can access a private member of the base class provided an instance of the derived class.

    Or:

    It seems that a class befriended to another class can access the private members of its instances.

    This relates to your question:

    In short, how is d.b_var valid within F::func(D& d) ?

    Because d.b_var is a member of an instance of class B (via polymorphism) to which instances of class F have access (via friend-status).

    This doesn't work with d.d_var, because the friendship to the base class is not inherited and instances of class F therefore don't have access to private members of d.

    This doesn't work with private (or protected) inheritance, because thereby another "layer of access restriction" is added. In addition, you then need to grant access to the derived class' privately inherited members as well (which d.b_var then is). For example by making also D a friend to F.

    For reference:

    • http://en.cppreference.com/w/cpp/language/friend
    • http://en.cppreference.com/w/cpp/language/derived_class
    0 讨论(0)
提交回复
热议问题