继续在 C++ 错误中学到的东西:基函数受到保护...
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
inherits ,则实例内的Control
受保护成员只能访问, 和, 和(假设不禁止)实例的实际动态类型和任何中间基。Control
Button
Control
Button
Button
这个漏洞颠覆了那个合同,完全违背了规则 11.4p1 的精神:
当非静态数据成员或非静态成员函数是其命名类的受保护成员时,将应用超出第 11 条中所述的附加访问检查。如前所述,授予对受保护成员的访问权限是因为引用发生在某个类的朋友或成员中
C
。如果访问要形成指向成员的指针(5.3.1),则嵌套名称说明符应表示C
或派生自 的类C
。所有其他访问都涉及(可能是隐式的)对象表达式。在这种情况下,对象表达式的类应该是C
或派生自的类C
。
感谢 AndreyT 链接http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#203,它提供了更多激励改变的例子,并呼吁由 Evolution 提出这个问题工作小组。