2011 C++ 标准在第 11.4 节中指出
当非静态数据成员或非静态成员函数是其命名类 (11.2) 的受保护成员 115 如前所述,授予对受保护成员的访问权限是因为引用发生在某个 C 类的朋友或成员中。如果访问要形成指向成员的指针(5.3.1),则嵌套名称说明符应表示 C 或从 C 派生的类。所有其他访问都涉及(可能是隐式的)对象表达式( 5.2.5 )。在这种情况下,对象表达式的类应该是 C 或从 C 派生的类。
(在旧标准中,第 11.5 节有类似的措辞。)
该规则限制了经常重复的想法,即“任何 B 或 B 的派生类都可以访问 B 的受保护成员”。然而,解释规则是困难的,不同的当前编译器执行规则的方式不同就证明了这一点。
例如,请参阅此测试代码。我使用 Apple LLVM Compiler 4.1、GCC 4.7.2 和 Visual Studio 2010 编译了这段代码。它们报告的错误既有相似之处,也有不同之处。
class Base
{
protected:
int A;
};
class Derived : public Base
{
protected:
int B;
};
class Grandchild : public Derived
{
void access_protected(Base* b, Derived* d,
Grandchild* g, class GreatGrandchild* gg );
};
class GreatGrandchild : public Grandchild {};
void Grandchild::access_protected(Base* b, Derived* d,
Grandchild* g, GreatGrandchild* gg )
{
int* p;
Base lb;
Derived ld;
Grandchild lg;
GreatGrandchild lgg;
A = 1; // Legal...
B = 2;
Base::A = 1;
Derived::B = 2;
b->A = 1; // Illegal ALL
p = &(b->A); // Illegal ALL
lb.A = 1; // Illegal ALL
p = &(lb.A); // Illegal ALL
d->A = 1; // Illegal GCC, VS
p = &(d->A); // Illegal GCC, VS
ld.A = 1; // Illegal GCC, VS
p = &(ld.A); // Illegal GCC, VS
d->B = 2; // Illegal ALL
p = &(d->B); // Illegal ALL
ld.B = 2; // Illegal ALL
p = &(ld.B); // Illegal ALL
g->A = 1; // Legal...
g->B = 2;
lg.A = 1;
lg.B = 2;
gg->A = 1;
gg->B = 2;
lgg.A = 1;
lgg.B = 2;
}
从这些结果中我了解到:(1)访问您自己的类和派生类的受保护成员总是可以的;(2) 访问声明它们的基类的受保护成员始终是非法的,但从该类除外;(3) 尽管标准注意区分指向成员的指针和“对象表达式”,但标准和编译器都给了它们相同的限制;Derived
(4) 目前尚不清楚访问“中间”基类(在示例中)的受保护成员是否合法,该成员在中间基类中声明。
那么,令人困惑的是,我是否可以谈论我的祖父母的受保护成员归我父母所有。没有双关语的意思。
friends
(为了简单和理智,我忽略了。)
由于protected
它是该语言的基本组成部分,因此我有动力去理解它。请:
- 根据您对标准的解释,哪个编译器正确实现了这一点?
- 整体限制的理由是什么?为什么我不能自由访问基类的受保护成员?它旨在避免什么具体错误?您是否知道在线讨论(理想情况下由标准委员会举行)来探讨这一基本原理?