我解释标准的方式,示例代码格式正确。(是的,这些friend
声明与@Steve Townsend 引用的内容有很大不同。)
private
11.2p1:如果使用访问说明符将一个类声明为另一个类的基类,则基类的public
和protected
成员可以作为private
派生类的成员访问。
11.2p4:当在类N中命名时,成员m是可访问的,如果
- m作为N的成员是公开的,或者
- m作为N的成员是私有的,并且引用发生在类N的成员或朋友中,或者
- 作为N的成员的m是受保护的,并且引用发生在类N的成员或朋友中,或在派生自N的类P的成员或朋友中,其中作为P的成员的m是私有的或受保护的,或
- 存在一个N的基类B可在引用点访问,并且m在类B中命名时可访问。
11.4p1:类的朋友是一个函数或类,它不是该类的成员,但被允许使用该类的私有和受保护的成员名称。
在第 11 条(成员访问控制)中没有任何陈述暗示一个类的朋友的访问权限比与它成为朋友的类少。请注意,“可访问”仅在特定类的上下文中定义。尽管我们有时通常会谈论成员或基类是“可访问的”或“不可访问的”,但谈论它是“在所有上下文中都可以访问”还是“在所有类中都可以访问”会更准确(就像这种情况一样)仅public
在使用时)。
现在介绍在自动定义的方法中对访问控制进行检查的部分。
12.1p7:隐式声明的类的默认构造函数在用于创建其类类型(1.8)的对象时被隐式定义。隐式定义的默认构造函数执行类的一组初始化,这些初始化将由用户编写的具有空mem-initializer-list (12.6.2) 和空函数体的类的默认构造函数执行。如果该用户编写的默认构造函数格式不正确,则程序格式错误。
12.6.2p6:表示虚拟基类的所有子对象都由最派生类(1.8)的构造函数初始化。如果最派生类的构造函数没有为虚拟基类指定mem-initializerV
,则V
调用其默认构造函数来初始化虚拟基类子对象。如果V
没有可访问的默认构造函数,则初始化格式错误。
12.4p5:隐式声明的析构函数在用于销毁其类类型的对象时被隐式定义(3.7)。如果隐式定义析构函数的类具有以下特征,则程序是非良构的:
- 具有不可访问的析构函数的类类型(或其数组)的非静态数据成员,或
- 具有不可访问的析构函数的基类。
12.8p7:如果隐式声明的复制构造函数用于从其类类型的对象的副本或从其类类型派生的类类型的副本初始化其类类型的对象,则它是隐式定义的。[注意:复制构造函数是隐式定义的,即使实现省略了它的使用 (12.2)。] 如果隐式定义了复制构造函数的类具有以下内容,则程序是非良构的:
- 具有不可访问或不明确的复制构造函数的类类型(或其数组)的非静态数据成员,或
- 具有不可访问或模棱两可的复制构造函数的基类。
12.8p12:如果隐式定义了复制赋值运算符的类具有:
- 类型的非静态数据成员
const
,或
- 引用类型的非静态数据成员,或
- 具有不可访问的复制赋值运算符的类类型(或其数组)的非静态数据成员,或
- 具有不可访问的复制赋值运算符的基类。
所有这些提到“不可访问”或“可访问”的要求都必须在某个类的上下文中进行解释,唯一有意义的类是隐式定义了成员函数的类。
在原始示例中,class A
隐式具有public
默认构造函数、析构函数、复制构造函数和复制赋值运算符。到 11.2p4,由于class C
是 的朋友class A
,所有这些成员在类中命名时都可以访问C
。因此,对这些成员的访问检查class A
不会导致class C
' 的默认构造函数、析构函数、复制构造函数或复制赋值运算符的隐式定义格式错误。