朋友 class 对象可以访问派生 class 对象的基础 class 私有成员吗?

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

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

似乎与(公开继承的)基 class 成为朋友的 class 可以访问基 class 的成员,前提是派生 class 的实例.

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

简而言之,d.b_var 如何在 F::func(D& d) 内有效?

#include <iostream>
#include <string>
using namespace std;

class B{
    int b_var;
    friend class F;
};

class D: public B{
    int d_var;
};

class F{
    public:
        void func(D &d){
            d.b_var = 5; 
        }
};

int main()
{
    cout<<"fine";
}
当使用 public 继承时,

DB。所以访问 b_var 仍然是完全合法的。
但是,如果您尝试访问 d_var,则会出现错误,因为正如您似乎知道的那样,友谊本身不会被继承。

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

你写:

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

但应该是:

似乎与(公共继承的)基 class 友好的 class 可以访问基 class 的私有成员,前提是派生 class.

或者:

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

这与您的问题有关:

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

因为 d.b_var 是 class B 实例的成员(通过多态性)class F 的实例可以访问(通过好友状态)。

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

这不适用于私有(或受保护)继承,因为由此添加了另一个 "layer of access restriction"。此外,您还需要授予对派生的 class' 私有继承成员的访问权限(然后是 d.b_var)。例如,让 D 也成为 F 的朋友。

供参考:

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

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

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

它仍然是一个B对象,也是一个D对象。它扩展了 B 可以这么说。 F 仍然可以从 B 访问该部分,因为它仍然存在,但不能从 D.

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

  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 可能会产生误导。更准确地说(另一种看待它的方式),b_var 不是派生 class D 的(直接)成员。相反,D 的对象包含基 class B 的子对象,它有成员 b_var,并且可以被朋友 F 访问。 (因为我们也可以将 d.b_var 写成 d.B::b_var。)

$10/3 派生 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 ]

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

因为

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 是 class D 的朋友,它也可以访问 D 的所有 private/protected 成员,但是不包括子对象 B 中的成员,因为它们不是 class D.

的(直接)成员

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

part containing members of B 
part containing members of D

这就是为什么对象切片的概念在我们这样做时起作用的原因:

D objD;
B objB = objD;

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

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

实际上,每个 class 里面有三个部分 w.r.t 可访问性:

public
protected
private 

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

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

然后在 class B 中创建以下 3 个部分,如上所示。

现在当我们声明一些 class Fclass Bfriend 时:

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

请注意,对于 class Fmember functionsint a;int b; 现在是 public。

现在,当 class Dclass B 派生 publicly 时,class Bpublic 部分变为 [=16 的 public 部分=].类似地,class Bprotected 部分变为 class Dprotected 部分。因此,class Bpublic 节部分可以通过 class D 的对象访问。由于 B::a;B::b;members functions of class F 的 public 部分中,因此 B::aB::b 可以通过 [=16= 的对象访问].另请注意,虽然推导后 int a;int b; 成为 class D 的成员,但编译器仍然能够区分它们并将它们视为 part of class B.

现在,当 class Dclass B 派生 privately 时,class Bpublic 部分变为 [=16 的 private 部分=].类似地,class Bprotected 部分成为 class D 的受保护部分。因此,现在 class B 中的 public 节部分不能 通过 class D 的对象访问。回想一下,在 class B 中,B::a;B::b; 最初位于 members functions of class F 的 public 部分,但在 private 推导之后,[=22 的成员=] 即 B::aB::b 现在位于 class D 的私人部分。因此,B::aB::b不能通过class D的对象访问。另请注意,虽然推导后 int a;int b; 成为 class D 的成员,但编译器仍然能够区分它们并将它们视为 part of class B。派生后 class B 的一些成员的可访问性和规则发生了变化。

由于这个问题与public, protected and private推导的效果有些关系,因此为了完整性请看: Why can a derived class not access a protected member of its base class through a pointer to base?