在c++ 11中,protected意味着public
In C++11, protected means public?
继续c++错误: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;
}
- http://ideone.com/pEXk9W
- http://ideone.com/kKlTvD
- http://ideone.com/zJX5U6
该是否真的符合标准要求?
(我认为这是一个缺陷,并声称&Gimme::i
的类型实际上应该是int Gimme::*
,即使i
是基类的成员。)但是我在标准中没有看到这样的内容,有一个非常具体的例子可以说明这一点。
我意识到有些人可能会对第三个注释方法(第二个ideone测试用例)实际上失败感到惊讶。这是因为考虑受保护的正确方式不是"我的派生类有访问权,其他人没有",而是"如果你从我派生,你将有权访问包含在你的实例中的这些继承变量,除非你授予它,否则其他人无权访问"。例如,如果Button
继承了Control
,那么Button
实例中Control
的受保护成员只能被Control
和Button
访问,并且(假设Button
没有禁止)实例的实际动态类型和任何中间基。
这个漏洞颠覆了契约,完全违背了规则的精神。
当非静态数据成员或非静态成员函数是其命名类的受保护成员时,将应用第11条前面所述的额外访问检查。如前所述,对受保护成员的访问被授予,因为引用发生在某个类
C
的友元或成员中。如果访问要形成指向成员(5.3.1)的指针,嵌套名称说明符应表示C
或从C
派生的类。所有其他访问都涉及(可能隐式的)对象表达式。在这种情况下,对象表达式的类应该是C
或从C
派生的类。
感谢AndreyT的链接http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#203,它提供了更多的例子来激励改变,并呼吁由进化工作组提出这个问题。
同样相关的是:GotW 76:访问权的使用和滥用
我看到过这种技术,我称之为"受保护的黑客",在这里和其他地方提到过很多次。是的,这种行为是正确的,并且确实是一种绕过受保护访问而不诉诸任何"肮脏"黑客的合法方法。
当m
是类Base
的成员时,使&Derived::m
表达式产生Derived::*
类型的指针的问题是类成员指针是逆变,而不是协变。这将使生成的指针不能用于Base
对象。例如,以下代码编译
struct Base { int m; };
struct Derived : Base {};
int main() {
int Base::*p = &Derived::m; // <- 1
Base b;
b.*p = 42; // <- 2
}
,因为&Derived::m
产生int Base::*
的值。如果它产生一个int Derived::*
值,代码将在第1行编译失败。如果我们试图用
int Derived::*p = &Derived::m; // <- 1
,它将在第2行编译失败。使其编译的唯一方法是执行强制强制类型转换
。 b.*static_cast<int Base::*>(p) = 42; // <- 2
这是不好的
注:我同意,这不是一个很有说服力的例子("只要从一开始就使用&Base:m
,问题就解决了")。然而,http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#203有更多的信息,可以解释为什么最初做出这样的决定。他们国家
04/00会议记录:
当前处理的基本原理是允许最广泛的给定成员地址表达式的可能用法。自指向基的指针成员可以隐式地转换为指针到派生成员,使表达式的类型为a指向基成员的指针允许对结果进行初始化或赋值指向指向基的成员或指向派生的成员。接受这个建议将只允许后者使用。
关于c++中的访问说明符,要记住的主要事情是它们控制名称可以在哪里使用。它实际上不做任何事情来控制对对象的访问。在c++上下文中,"对成员的访问"意味着"使用名称的能力"。
观察:
class Encapsulator {
protected:
int i;
};
struct Gimme : Encapsulator {
using Encapsulator::i;
};
int main() {
Encapsulator e;
std::cout << e.*&Gimme::i << 'n';
}
e.*&Gimme::i
是允许的,因为它根本不访问受保护的成员。我们正在访问using
声明在Gimme
内部创建的成员。也就是说,即使using
声明并不意味着在Gimme
实例中有任何额外的子对象,它仍然会创建一个额外的成员。成员和子对象不是一回事, Gimmie::i
是一个不同的公共成员,可以用来访问与受保护成员Encapsulator::i
相同的子对象。
一旦理解了"类的成员"answers"子对象"之间的区别,就应该清楚,这实际上并不是11.4 p1规定的合同的漏洞或意外失效。
可以为不可命名的对象创建一个可访问的名称,或者以其他方式提供对不可命名对象的访问,这是预期的行为,尽管它与其他一些语言不同,可能令人惊讶。
- C++核心准则 C35 对于接口类"A base class destructor should be either public and virtual, or protected and nonv
- C++错误:"error: int aaa::bbb is protected within this context"
- 这行代码在C++类中意味着什么
- std::make_shared和protected/private构造函数
- 这对"With a stackless coroutine, only the top-level routine may be suspended."意味着什么
- 寻找地理和伤害意味着超载
- @CPPFLAGS@在 Makefile.in 中意味着什么?
- 在C++标准中做格式好意味着代码可以编译
- 继承/多态性 - 我是否被迫使用"protected"变量?
- 生成文件"relink"意味着什么?
- 从二进制流中读取时,将双精度变量的地址转换为 char* 意味着什么?
- 在这种情况下,"typename..."意味着什么?
- C++评估顺序优化是否意味着对不同的操作数使用不同的内核?
- "in-situ without memory allocation" 字符串的愚蠢实现意味着什么?
- 使用typedef有什么用,它意味着什么
- GCC 的 -Wpsabi 选项究竟有什么作用?压制它意味着什么?
- 负指数是否必然意味着未定义的行为
- c++ 提升如果 .extension() == " "这意味着这个文件是一个文件夹?
- 这在C++ "It does not own the underlying data, and so is cheap to copy or assign"中意味着什么
- 在c++ 11中,protected意味着public