朋友类对象可以在派生类对象上访问基类私有成员吗?

时间:2016-09-08 06:56:39

标签: c++ inheritance private friend

我很惊讶下面的代码编译。

似乎一个与(公共继承的)基类成为朋友的类可以访问基类的成员,提供派生类的实例。

如果继承更改为private,则编译失败。

简而言之,d.b_var的{​​{1}}如何有效?

F::func(D& d)

5 个答案:

答案 0 :(得分:6)

class D的对象由两个独立的部分组成:

part containing members of B 
part containing members of D

当我们这样做时,为什么对象切片的概念会起作用:

D objD;
B objB = objD;

现在我们可以从object of class D内部访问,part containing members of B来自objB。编译器记住或可以区分class D内的两个部分。所以编译器知道通过什么访问什么。

friend class F;中的语句class B只是告诉member functions of class F可以访问private, protected and public class B成员。也就是说,对于member functions of class Fclass B的所有成员都是public

实际上,在每个课程中都有三个部分w.r.t可访问性:

public
protected
private 

所以当我们声明一些class B时:

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

然后在类B内创建3个部分,如上所示。

现在,当我们宣布某些class Ffriend class B时:

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

然后编译器按如下方式创建部分:

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             
};

请注意,int a;int b;现已公开member functions class F

现在class D publicly来自class Bpublic class B部分成为public的{​​{1}}部分。同样,class D的{​​{1}}部分成为protected的{​​{1}}部分。因此,可以通过class B的对象访问protected的{​​{1}}部分。由于class Dpublic位于class B的公开部分,因此可以通过class D的对象访问B::a;B::b;。另请注意,虽然在派生members functions of class FB::a成为B::b的成员之后,仍然编译器能够区分它们并将它们视为class D

现在int a; int b;来自class Dpart of class B class D部分成为privately的{​​{1}}部分。同样,class B的{​​{1}}部分成为public的受保护部分。因此,现在class B 内的private部分无法通过class D的对象访问。回想一下,在protected中,class Bclass D最初位于public的公开部分,但在class B推导后,class D的成员即{ {1}}和class B现在位于B::a;的私有部分。因此,B::b;members functions of class F 无法通过private的对象访问。另请注意,虽然在派生class BB::a成为B::b的成员之后,仍然编译器能够区分它们并将它们视为class D。在推导之后,B::a的某些成员的可访问性和规则已经改变。

由于这个问题在某种程度上与B::b推导的效果有关,因此为了完整性,请参阅: Why can a derived class not access a protected member of its base class through a pointer to base?

答案 1 :(得分:5)

使用公共继承时,

DB。因此,访问b_var仍然完全合法 但是,如果您尝试访问d_var,则会收到错误,因为您似乎意识到友谊本身并未继承。

继承始终使基类的所有成员都是派生的成员。访问说明符仅影响标识符可见的位置。这就是为什么非法访问私人会员会产生与访问不存在的标识符不同的错误。

答案 2 :(得分:4)

  
      
  1. 似乎友谊是以某种方式继承的,而朋友类可以访问派生类的成员   简而言之,d.b_var
  2. F::func(D& d)的有效性如何?   

d.b_var可能会产生误导。更准确(另一种方式),b_var不是派生类D的(直接)成员。相反,D的对象包含基类B的子对象,其具有成员b_var,并且可以由朋友F访问。 (我们也可以将d.b_var写为d.B::b_var。)

$10/3 Derived classes [class.derived]

  

base-specifier-list指定基类的类型   包含在派生类类型的对象中的子对象。 [   例如:

struct Base {
  int a, b, c;
};

struct Derived : Base {
  int b;
};

struct Derived2 : Derived {
  int c;
};
     

这里,类Derived2的对象将具有类的子对象   Derived反过来会有一个类Base的子对象。 - 结束   例子]

并且

  
      
  1. 如果继承更改为private,则编译失败。
  2.   

由于

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
    }
};

然后

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.
    }
};

请注意,在最后一种情况下,即使F是类D的朋友,它也可以访问D的所有私有/受保护成员,但不包括子对象{{1}中的成员因为他们不是班级B的(直接)成员。

答案 3 :(得分:3)

虽然我已经有了很好的答案,但我认为有些图片也会有所帮助。

B-class

这是您的B课程的抽象。 F可以访问其所有成员。

当您现在实例化D对象时,它看起来像这样

D-class

它仍然是B对象,但也是D对象。它可以说是BF仍然可以从B访问该部分,因为它仍在那里,但不是来自D

请注意,这些抽象并没有真正显示内存中的布局并解释覆盖等等。但它们只是为了这个问题。

答案 4 :(得分:1)

你写道:

  

似乎友谊是以某种方式继承的,而朋友类可以访问派生类的成员。

但它应该是:

似乎与(公共继承的)基类成熟的类可以访问基类的私有成员,提供派生类的实例。

或者:

似乎与另一个类成为朋友的类可以访问其实例的私有成员。

这与你的问题有关:

  

简而言之,d.b_var的{​​{1}}如何有效?

因为F::func(D& d)是B类实例(通过多态)的成员,F类的实例可以访问(通过friend-status)。

这不适用于d.b_var,因为与基类的友谊不会被继承,因此F类的实例无法访问d的私有成员。

这不适用于私有(或受保护)继承,因为这样就可以实现另一个访问限制层"被添加。此外,您还需要授予对派生类的访问权限。私人继承的成员(当时d.d_var)。例如,也让D成为F的朋友。

供参考: