我们都知道基类中指定的protected
成员只能从派生类自己的实例访问。这是标准的一个特性,这已在Stack Overflow上多次讨论过:
但似乎可以使用成员指针来解决这个限制,例如用户chtz has shown me:
struct Base { protected: int value; };
struct Derived : Base
{
void f(Base const& other)
{
//int n = other.value; // error: 'int Base::value' is protected within this context
int n = other.*(&Derived::value); // ok??? why?
(void) n;
}
};
为什么这可能,在实施或标准的措辞中是某个想要的功能还是故障?
从评论中出现了另一个问题:if Derived::f
is called with an actual Base
,是不确定的行为?
答案 0 :(得分:29)
由于访问控制 expr.ref,无法使用类成员访问 [class.access](aclass.amember
)访问成员不会使用其他表达式使该成员无法访问。
表达式&Derived::value
(whose type is int Base::*
)完全符合标准,并指定value
的成员Base
。然后,表达式a_base.*p
p
表示Base
成员的指针a_base
Base
other.*(&Derived::value);
value
。{/ {}} p>
因此,任何符合标准的编译器都应使表达式other
定义为行为:访问ionic cordova platform update android
的成员ionic cordova platform add android@latest
。
答案 1 :(得分:14)
是黑客吗?
与使用reinterpret_cast
类似,这可能很危险,并且可能是很难找到错误的来源。但它已经形成良好,毫无疑问它是否应该有效。
澄清类比:reinterpret_cast
的行为也完全在标准中指定,可以在没有任何UB的情况下使用。但是reinterpret_cast
规避了类型系统,类型系统是有原因的。类似地,这个指向成员技巧的指针很好地根据标准形成,但它绕过了成员的封装,并且封装(通常)存在是有原因的(我通常说,因为我认为程序员可以轻率地使用封装)。 / p>
[是]在执行中的某个地方还是标准的措辞?
不,实施是正确的。这就是指定语言的工作方式。
Derived
的成员函数显然可以访问&Derived::value
,因为它是基础的受保护成员。
该操作的结果是指向Base
成员的指针。这可以应用于对Base
的引用。成员访问权限不适用于指向成员的指针:它仅适用于成员的名称。
从评论中出现了另一个问题:如果使用实际的Base调用Derived :: f,它是否是未定义的行为?
不是UB。 Base
有会员。
答案 2 :(得分:-1)
只是为了添加答案并放大我可以在你的行之间阅读的恐怖片段。如果你认为访问说明符是“法律”,那就是为了防止你做坏事,我认为你错过了这一点。 public
,protected
,private
,const
...都是系统的一部分,对C ++来说是一个巨大的优势。没有它的语言可能有许多优点,但是当你构建大型系统时,这些东西才是真正的资产。
话虽如此:我认为可以绕过提供给你的几乎所有安全网是件好事。只要你记得那可能的'并不意味着“好”。这就是为什么它永远不应该是“容易”的原因。但对于其他人 - 它取决于你。你是建筑师。
多年前我可以简单地做到这一点(它可能仍然适用于某些环境):
#define private public
非常有用的敌对'外部头文件。好的做法?你怎么看?但有时你的选择是有限的。
所以是的,你所展示的是系统中的一种破坏。但是,嘿,是什么让你无法获得并向会员分发公开引用?如果可怕的维护问题让你失望 - 无论如何,为什么不呢?
答案 3 :(得分:-2)
基本上你正在做的是欺骗编译器,这应该有效。我总是看到这样的问题,人们有时会得到不好的结果,有时它会起作用,这取决于它如何转换为汇编程序代码。
我记得在一个整数上看到一个带有const
关键字的案例,但随后有了一些技巧,这个人能够改变价值并成功规避了编译器的意识。结果是:一个简单的数学运算的错误值。原因很简单:x86中的汇编确实区分了常量和变量,因为某些指令在其操作码中确实包含常量。因此,由于编译器认为它是一个常量,它会将它视为一个常量并以错误的CPU指令以优化的方式处理它,并且baam,你得到的数字有一个错误
换句话说:编译器将尝试强制执行它可以强制执行的所有规则,但最终可能会欺骗它,并且您可能会或可能不会根据您尝试执行的操作得到错误的结果,因此您会更好只有当你知道自己在做什么时才做这些事情。
在您的情况下,指针&Derived::value
可以从对象计算出从类的开头有多少字节。这基本上是编译器访问它的方式,因此,编译器:
value
访问derived
。derived
具有相同结构的对象中获取字节偏移量(显然,base
)。所以,你没有违反任何规则。您成功绕过了编译规则。你不应该这样做,完全是因为你所附链接中描述的原因,因为它打破了OOP封装,但是,如果你知道你正在做什么......