从这个问题How is access for private variables implemented in C++ under the hood?的讨论中我提出了一个变体:可以通过强制转换并依赖布局兼容性来调用私有成员函数,而不是访问私有数据成员吗?
一些代码(灵感来自Herb Sutter的专栏Uses and Abuses of Access Rights)
#include <iostream>
class X
{
public:
X() : private_(1) { /*...*/ }
private:
int Value() { return private_; }
int private_;
};
// Nasty attempt to simulate the object layout
// (cross your fingers and toes).
//
class BaitAndSwitch
// hopefully has the same data layout as X
{ // so we can pass him off as one
public:
int Value() { return private_; }
private:
int private_;
};
int f( X& x )
{
// evil laughter here
return (reinterpret_cast<BaitAndSwitch&>(x)).Value();
}
int main()
{
X x;
std::cout << f(x) << "\n"; // prints 0, not 1
return 0;
};
注意:这有效(至少在Ideone上)!新的C++11 Standard是否有任何方式通过依赖布局兼容性和reinterpret_cast /来提供保证或至少实现定义的方式来绕过访问控制的static_cast?
EDIT1 :Ideone
上的输出EDIT2 :在Sutter专栏中,他列出了上述代码无法保证工作的两个原因(虽然它在实践中有效)
a)不保证X和BaitAndSwitch的对象布局 同样,虽然在实践中他们可能总是如此。
b)reinterpret_cast的结果是未定义的,尽管大多数都是如此 编译器会让你尝试使用结果引用 黑客打算。
新的C ++ 11 Standard现在是否提供了这些layout / reinterpret_cast保证?
答案 0 :(得分:3)
是的,您可以创建一种类型,使用与您尝试窃取的类型相同的布局,然后reinterpret_cast
从该类型到您的布局兼容类型。但是,如果 ,源和目标类型都是标准布局类型,那么这只受标准保护(当然,只有它们的布局相同才能实际工作)。因此,如果源具有虚函数,则会被搞砸。
这似乎满足了萨特在这里的两个问题。标准布局规则确保两种类型都是标准布局,以相同的顺序定义相同的成员是布局兼容的(第9.2节,第17段):
如果两个标准布局结构(第9类)类型具有相同数量的非静态数据成员,并且相应的非静态数据成员(按声明顺序)具有布局兼容类型(3.9),则它们是布局兼容的。
reinterpret_cast
的规则指定了两种标准布局类型之间转换的含义(第5.2.10节,第7段):
可以将对象指针显式转换为不同类型的对象指针。 当“指向T1的指针”类型的prvalue v转换为“指向cv T2的指针”类型时,如果T1和T2都是标准布局类型(3.9)和T2的对齐要求,则结果为
static_cast<cv T2*>(static_cast<cv void*>(v))
并不比T1更严格,或者两种类型都无效。