通过成员指针访问受保护的成员:这是黑客攻击吗?
Access to protected member through member-pointer: is it a hack?
我们都知道从基础 class 指定 protected
的成员只能从派生的 class 自己的实例访问。这是标准中的一项功能,已在 Stack Overflow 上多次讨论:
- 无法从派生类型的范围访问另一个实例的受保护成员
;
- Why can't my object access protected members of another object defined in common base class?
- 还有其他人。
但似乎可以使用成员指针绕过此限制,因为用户 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
,这是未定义的行为吗?
基本上你所做的是欺骗编译器,这应该有效。我总是看到这类问题,有时人们会得到不好的结果,有时它会起作用,这取决于它如何转换为汇编代码。
我记得曾见过一个在整数上带有 const
关键字的案例,但后来通过一些技巧,这个人能够更改值并成功绕过编译器的感知。结果是:一个简单的数学运算的错误值。原因很简单:x86 中的汇编确实区分了常量和变量,因为某些指令在其操作码中确实包含常量。因此,由于编译器 认为 它是一个常量,它将把它视为一个常量并以优化的方式使用错误的 CPU 指令来处理它,baam,你结果数字有误。
换句话说:编译器将尝试执行它可以执行的所有规则,但您最终可能会欺骗它,并且根据您尝试执行的操作,您可能会或可能不会得到错误的结果,所以只有当你知道自己在做什么时,你才最好做这样的事情。
在您的例子中,指针 &Derived::value
可以根据从 class 开始的字节数从对象计算得出。这基本上就是编译器访问它的方式,因此,编译器:
- 没有发现任何权限问题,因为您正在 compile-time 通过
derived
访问 value
。
- 可以做到,因为您在与
derived
具有相同结构的对象中获取以字节为单位的偏移量(很明显,base
)。
所以,你没有违反任何规则。您成功规避了编译规则。你不应该这样做,正是因为你所附链接中描述的原因,因为它破坏了 OOP 封装,但是,好吧,如果你知道你在做什么......
is it a hack?
与使用 reinterpret_cast
类似,这可能很危险,并且可能成为难以发现错误的潜在来源。但它的格式很好,毫无疑问它是否应该工作。
澄清类比:reinterpret_cast
的行为也在标准中明确指定,可以在没有任何 UB 的情况下使用。但是 reinterpret_cast
绕过了类型系统,类型系统的存在是有原因的。
[Is it] a glitch somewhere in the implementation or the wording of the Standard?
不,实现是正确的。这就是指定语言的工作方式。
Derived
的成员函数显然可以访问 &Derived::value
,因为它是基类的受保护成员。
该操作的结果是指向 Base
成员的指针。这可以应用于对 Base
的引用。成员访问权限不适用于指向成员的指针:它仅适用于成员的名称。
From comments emerged another question: if Derived::f is called with an actual Base, is it undefined behaviour?
不是 UB。 Base
有会员
无法使用 class 成员访问 expr.ref (aclass.amember
) due to access control [class.access] 访问成员这一事实并不意味着无法使用其他表达式访问该成员。
表达式&Derived::value
(whose type is int Base::*
) is perfectly standard compliant, and it designates the member value
of Base
. Then the expression a_base.*p
where p
is a pointer to a member of Base
and a_base
an instance of Base
is also standard compliant.
因此任何符合标准的编译器都应使表达式 other.*(&Derived::value);
定义行为:访问 other
的成员 value
。
只是为了增加答案并放大一点我可以从你的字里行间读到的恐怖。如果您将访问说明符视为 'the law',监管您以阻止您执行 'bad things',我认为您没有抓住要点。 public
、protected
、private
、const
... 都是系统的一部分,对 C++ 来说是一个巨大的优势。没有它的语言可能有很多优点,但当你构建大型系统时,这些东西是真正的资产。
话虽如此:我认为可以绕过几乎所有提供给您的安全网是一件好事。只要你记住 'possible' 并不意味着 'good'。这就是为什么它永远不应该是 'easy' 的原因。但对于其余的 - 这取决于你。你是建筑师。
几年前我可以简单地这样做(并且它可能在某些环境中仍然有效):
#define private public
对 'hostile' 外部头文件很有帮助。好的做法?你怎么看?但有时你的选择是有限的。
所以是的,你显示的是 kind-of 系统漏洞。但是,嘿,是什么让你无法推导并分发 public 对成员的引用?如果可怕的维护问题让您兴奋 - 无论如何,为什么不呢?
我们都知道从基础 class 指定 protected
的成员只能从派生的 class 自己的实例访问。这是标准中的一项功能,已在 Stack Overflow 上多次讨论:
- 无法从派生类型的范围访问另一个实例的受保护成员 ;
- Why can't my object access protected members of another object defined in common base class?
- 还有其他人。
但似乎可以使用成员指针绕过此限制,因为用户 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
,这是未定义的行为吗?
基本上你所做的是欺骗编译器,这应该有效。我总是看到这类问题,有时人们会得到不好的结果,有时它会起作用,这取决于它如何转换为汇编代码。
我记得曾见过一个在整数上带有 const
关键字的案例,但后来通过一些技巧,这个人能够更改值并成功绕过编译器的感知。结果是:一个简单的数学运算的错误值。原因很简单:x86 中的汇编确实区分了常量和变量,因为某些指令在其操作码中确实包含常量。因此,由于编译器 认为 它是一个常量,它将把它视为一个常量并以优化的方式使用错误的 CPU 指令来处理它,baam,你结果数字有误。
换句话说:编译器将尝试执行它可以执行的所有规则,但您最终可能会欺骗它,并且根据您尝试执行的操作,您可能会或可能不会得到错误的结果,所以只有当你知道自己在做什么时,你才最好做这样的事情。
在您的例子中,指针 &Derived::value
可以根据从 class 开始的字节数从对象计算得出。这基本上就是编译器访问它的方式,因此,编译器:
- 没有发现任何权限问题,因为您正在 compile-time 通过
derived
访问value
。 - 可以做到,因为您在与
derived
具有相同结构的对象中获取以字节为单位的偏移量(很明显,base
)。
所以,你没有违反任何规则。您成功规避了编译规则。你不应该这样做,正是因为你所附链接中描述的原因,因为它破坏了 OOP 封装,但是,好吧,如果你知道你在做什么......
is it a hack?
与使用 reinterpret_cast
类似,这可能很危险,并且可能成为难以发现错误的潜在来源。但它的格式很好,毫无疑问它是否应该工作。
澄清类比:reinterpret_cast
的行为也在标准中明确指定,可以在没有任何 UB 的情况下使用。但是 reinterpret_cast
绕过了类型系统,类型系统的存在是有原因的。
[Is it] a glitch somewhere in the implementation or the wording of the Standard?
不,实现是正确的。这就是指定语言的工作方式。
Derived
的成员函数显然可以访问 &Derived::value
,因为它是基类的受保护成员。
该操作的结果是指向 Base
成员的指针。这可以应用于对 Base
的引用。成员访问权限不适用于指向成员的指针:它仅适用于成员的名称。
From comments emerged another question: if Derived::f is called with an actual Base, is it undefined behaviour?
不是 UB。 Base
有会员
无法使用 class 成员访问 expr.ref (aclass.amember
) due to access control [class.access] 访问成员这一事实并不意味着无法使用其他表达式访问该成员。
表达式&Derived::value
(whose type is int Base::*
) is perfectly standard compliant, and it designates the member value
of Base
. Then the expression a_base.*p
where p
is a pointer to a member of Base
and a_base
an instance of Base
is also standard compliant.
因此任何符合标准的编译器都应使表达式 other.*(&Derived::value);
定义行为:访问 other
的成员 value
。
只是为了增加答案并放大一点我可以从你的字里行间读到的恐怖。如果您将访问说明符视为 'the law',监管您以阻止您执行 'bad things',我认为您没有抓住要点。 public
、protected
、private
、const
... 都是系统的一部分,对 C++ 来说是一个巨大的优势。没有它的语言可能有很多优点,但当你构建大型系统时,这些东西是真正的资产。
话虽如此:我认为可以绕过几乎所有提供给您的安全网是一件好事。只要你记住 'possible' 并不意味着 'good'。这就是为什么它永远不应该是 'easy' 的原因。但对于其余的 - 这取决于你。你是建筑师。
几年前我可以简单地这样做(并且它可能在某些环境中仍然有效):
#define private public
对 'hostile' 外部头文件很有帮助。好的做法?你怎么看?但有时你的选择是有限的。
所以是的,你显示的是 kind-of 系统漏洞。但是,嘿,是什么让你无法推导并分发 public 对成员的引用?如果可怕的维护问题让您兴奋 - 无论如何,为什么不呢?