6

从这个问题的讨论中,如何在 C++ 中实现私有变量的访问?我提出了一种变体:可以通过强制转换和依赖布局兼容性来调用私有成员函数,而不是访问私有数据成员吗?

一些代码(灵感来自 Herb Sutter 的专栏Uses and Abuse 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 标准有什么方法可以通过依赖布局兼容性和 reinterpret_cast / static_cast提供有保证的或至少是实现定义的方法来规避访问控制?

EDIT1:Ideone 上的输出

EDIT2:在 Sutter 的专栏中,他列出了上述代码不能保证有效的两个原因(尽管它在实践中有效)

a) X 和 BaitAndSwitch 的对象布局不能保证相同,尽管实际上它们可能总是相同。

b) reinterpret_cast 的结果是未定义的,尽管大多数编译器会让你尝试按照黑客的意图使用生成的引用。

新的 C++11 标准现在是否提供这些 layout / reinterpret_cast 保证?

4

1 回答 1

3

是的,您可以创建一个使用与您试图窃取的类型相同的布局的类型,然后reinterpret_cast从该类型到您的布局兼容类型。但这只有在源类型和目标类型都是标准布局类型时才受标准保护当然,只有在它们的布局相同时它才真正起作用)。因此,如果源具有虚拟功能,那么您就完蛋了。

这似乎满足了 Sutter 的两个问题。标准布局的规则确保了两种以相同顺序定义相同成员的标准布局的类型是布局兼容的(第 9.2 节,第 17 段):

如果两个标准布局结构(第 9 条)类型具有相同数量的非静态数据成员并且相应的非静态数据成员(按声明顺序)具有布局兼容类型(3.9),则它们是布局兼容的。

以及指定两种标准布局类型之间转换含义的规则reinterpret_cast(第 5.2.10 节,第 7 段):

对象指针可以显式转换为不同类型的对象指针。当“pointer to T1”类型的prvalue v转换为“pointer to cv T2”类型时,结果是static_cast<cv T2*>(static_cast<cv void*>(v))如果T1和T2都是标准布局类型(3.9)并且T2的对齐要求不比那些更严格T1 的,或者如果任一类型为无效。

于 2012-07-15T18:26:30.737 回答