我很惊讶下面的代码编译。
似乎一个与(公共继承的)基类成为朋友的类可以访问基类的成员,提供派生类的实例。
如果继承更改为private
,则编译失败。
简而言之,d.b_var
的{{1}}如何有效?
F::func(D& d)
答案 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 F
,class B
的所有成员都是public
。
实际上,在每个课程中都有三个部分w.r.t可访问性:
public
protected
private
所以当我们声明一些class B
时:
class B
{
public:
int a;
protected:
int b;
public:
int c;
};
然后在类B
内创建3个部分,如上所示。
现在,当我们宣布某些class F
为friend
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 B
,public
class B
部分成为public
的{{1}}部分。同样,class D
的{{1}}部分成为protected
的{{1}}部分。因此,可以通过class B
的对象访问protected
的{{1}}部分。由于class D
和public
位于class B
的公开部分,因此可以通过class D
的对象访问B::a;
和B::b;
。另请注意,虽然在派生members functions of class F
和B::a
成为B::b
的成员之后,仍然编译器能够区分它们并将它们视为class D
。
现在int a;
int b;
来自class D
,part of class B
class D
部分成为privately
的{{1}}部分。同样,class B
的{{1}}部分成为public
的受保护部分。因此,现在class B
内的private
部分无法通过class D
的对象访问。回想一下,在protected
中,class B
和class D
最初位于public
的公开部分,但在class B
推导后,class D
的成员即{ {1}}和class B
现在位于B::a;
的私有部分。因此,B::b;
和members functions of class F
无法通过private
的对象访问。另请注意,虽然在派生class B
和B::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)
D
是B
。因此,访问b_var
仍然完全合法
但是,如果您尝试访问d_var
,则会收到错误,因为您似乎意识到友谊本身并未继承。
继承始终使基类的所有成员都是派生的成员。访问说明符仅影响标识符可见的位置。这就是为什么非法访问私人会员会产生与访问不存在的标识符不同的错误。
答案 2 :(得分:4)
- 似乎友谊是以某种方式继承的,而朋友类可以访问派生类的成员 简而言之,
中d.b_var
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
的子对象。 - 结束 例子]
并且
- 如果继承更改为
醇>private
,则编译失败。
由于
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
课程的抽象。 F
可以访问其所有成员。
当您现在实例化D
对象时,它看起来像这样
它仍然是B对象,但也是D对象。它可以说是B
。 F
仍然可以从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的朋友。
供参考: