深入了解C++受到保护
Getting to the bottom of C++ protected
2011 C++标准在第 11.4 节中指出
超出第 11 条前面描述的额外访问检查 在非静态数据成员或非静态成员函数时应用 是其命名类 ( 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)尽管标准注意区分指向成员的指针和"对象表达式",但标准和编译器都给了它们相同的限制;(4)目前尚不清楚访问"中间"基类(示例中Derived
)的受保护成员是否合法,该成员在中间基的基中声明。
那么,令人困惑的是,我是否可以谈论我祖父母的受保护成员归我父母所有。没有双关语的意图。
(为了简单和理智,我忽略了friends
。
由于protected
是语言的基本组成部分,因此我有动力很好地理解它。请:
- 根据您对标准的解释,哪个编译器正确实现了这一点?
- 总体限制的理由是什么?为什么我不能自由访问基类的受保护成员?它旨在避免什么具体错误?您是否知道在线讨论(最好是由标准委员会举行的)来探讨这一基本原理?
我会说GCC和Visual Studio是正确的。
鉴于以下情况:
class Base
{
protected:
int A;
};
class Derived : public Base
{
protected:
int B;
};
class OtherGrandchild : Derived
{
};
class Grandchild : Derived
{
void access_protected(OtherGrandchild* otherGrandchild);
};
void Grandchild::access_protected(OtherGrandchild* otherGrandchild)
{
otherGrandchild->A = 1; // Should be illegal
otherGrandchild->B = 1; // Should be illegal
Derived* derived = static_cast<Derived*>(otherGrandchild);
derived->A = 1; // Should still be illegal
derived->B = 1; // Should still be illegal
}
如果您不受限制,则只需强制转换为公共基类型,即可将OtherGrandchild
的私有成员从Grandchild
更改为私有成员。这种访问只能通过好友声明来允许。
我不确定是否有关于这个话题的任何讨论,但这是我对它的解释。
的原因是派生对象 D 只能访问基的受保护成员,使用的对象表达式是 D 或另一个类派生自 D。
标准不允许 D 使用类型 B 的对象表达式访问另一个对象的受保护成员
这就是上面引用想要表达的确切内容(大致确定)。
class base
{
protected:
int x;
};
class derived : public base
{
public:
void f(base *p)
{
x = 2; // good
p->x = 3; // not good. base is not 'derived' nor derived from 'derived'
}
};
int main() { }
想一想。基 B 的公共成员在任何派生类 D 中均可访问;B 的私有成员在任何派生类 D 中均不可访问。只有受保护的成员需要一些考虑。上述《标准》引述阐明了这一考虑。B 的受保护非静态成员只能在派生类 D 中访问,只能使用类型 D 的对象表达式或从 D 派生的类型
我不能引用参考或权威来源,但在我看来,g++ 和 VS 在这里都是正确的(这是他们第一次同意?),因为空的中间注入类可以改变父级受保护数据的访问控制根本没有逻辑意义。
至于基本原理,几乎可以肯定是因为限制越少protected
它就越像public
(它已经与公共有很多相似之处,因为你只需要派生一个类就可以不受限制地访问父内部)。如果您开始允许甚至不是同一实例的子类操纵您的状态,则违反类不变量的机会会大大增加。
- 有充分的理由在h文件中使用include保护而不是cpp文件吗
- 为什么在保护模式下继承升级不起作用
- 访问被拒绝后,c++中的故障保护代码
- C++:无法访问声明的受保护成员
- 为什么您需要C++头文件的包含保护
- lock_guard是否保护返回值
- 如何在GTK程序运行时禁用屏幕保护程序/电源管理/屏幕消隐
- 继承和友元函数,从基类访问受保护的成员
- 为什么派生类的好友不能使用受保护的成员?
- C++:为什么无法在派生类中访问受保护的构造函数?
- 公共/私有/受保护是否会更改内存中结构的排列?
- 所以我正在为我的学校作业练习继承,但我无法正确实施标题保护
- C2011: 'Card':"类"类型重新定义(尽管使用了包含保护并且没有在文件中重新定义.cpp类)
- 在父类中公开受保护的构造函数
- 如何从其他结构访问受保护的结构变量
- C++标头保护语法和标头放置
- 如何实现返回受保护结构的私有函数
- 相同的层次结构,访问基类的受保护成员时的行为不同
- 使用标头保护的多个定义链接错误
- 从模板化父类中的派生内部类访问受保护的成员变量