【发布时间】:2013-06-03 00:58:03
【问题描述】:
继续在C++ error: base function is protected 中学到的东西...
C++11 指向成员的指针规则有效地去除了任何值的 protected 关键字,因为可以在不相关的类中访问受保护的成员,而无需任何邪恶/不安全的强制转换。
也就是说:
class Encapsulator
{
protected:
int i;
public:
Encapsulator(int v) : i(v) {}
};
Encapsulator f(int x) { return x + 2; }
#include <iostream>
int main(void)
{
Encapsulator e = f(7);
// forbidden: std::cout << e.i << std::endl; because i is protected
// forbidden: int Encapsulator::*pi = &Encapsulator::i; because i is protected
// forbidden: struct Gimme : Encapsulator { static int read(Encapsulator& o) { return o.i; } };
// loophole:
struct Gimme : Encapsulator { static int Encapsulator::* it() { return &Gimme::i; } };
int Encapsulator::*pi = Gimme::it();
std::cout << e.*pi << std::endl;
}
这真的符合标准吗?
(我认为这是一个缺陷,并声称&Gimme::i 的类型确实应该是int Gimme::*,即使i 是基类的成员。但我在标准中看不到任何使其成为它的东西所以,有一个非常具体的例子说明了这一点。)
我意识到有些人可能会惊讶于第三种评论方法(第二个 ideone 测试用例)实际上失败了。那是因为正确思考受保护的方法不是“我的派生类可以访问,而没有其他人”,而是“如果您从我那里派生,您将可以访问您的实例中包含的这些继承变量,除非您授予它”。例如,如果Button 继承Control,则Button 实例中的Control 的受保护成员只能由Control 和Button 访问,并且(假设Button 不禁止它)实例的实际动态类型和任何中间基数。
这个漏洞颠覆了合同,完全违背了11.4p1的精神:
当非静态数据成员或非静态成员函数是其命名类的受保护成员时,将应用第 11 条中所述之外的附加访问检查。 如前所述,授予对受保护成员的访问权限,因为引用发生在朋友或某个类
C的成员中。如果访问要形成指向成员的指针 (5.3.1),则 nested-name-specifier 应表示C或派生自C的类。所有其他访问都涉及(可能是隐式的)对象表达式。在这种情况下,对象表达式的类应为C或派生自C的类。
感谢 AndreyT 链接 http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#203,它提供了更多激励更改的示例,并呼吁进化工作组提出该问题。
【问题讨论】:
-
是的,我看不出 C++11 中剥离了哪些保护。
-
@Charles:我并不是说 C++98 没有同样的漏洞。只是这存在于当前版本中。
-
@BenVoigt:也可以在不强制转换的情况下抢劫
private成员 -
@Andy:你能提供一个例子(不调用未定义的行为)吗?
-
@Ben:Johannes Schaub 想出了这个技巧。这是link
标签: c++ member member-access