2

我正在阅读一篇关于 C++ 如何没有字段访问器作为语言的一部分的文章。

在文章的最后,作者给出了一个基于宏的解决方案,可以模拟类的字段访问器:

// a little trick to fool compiler we are not accessing NULL pointer here
#define property_offset(type, name) \
  (((char*)&((type*)(0xffff))->name) - (char*)(0xffff))

#define property_parent(type, name) \
  ((type*)((char*)(this) - property_offset(type, name)))

// macro defining property
#define property(type, name, parent)                                         \
  struct name##_property {                                                   \
    operator type() { return property_parent(parent, name)->get_##name(); }  \
    void operator=(type v) { property_parent(parent, name)->set_##name(v); } \
                                                                             \
   private:                                                                  \
    char zero[0];                                                            \
  } name

// our main class
class Node {

  /* visitCount will act as a field accessor */
  property(int, visitCount, Node);
};

当我通过预处理器运行它时,我得到:

class Node {

  struct visitCount_property {
    operator int() { return ((Node*)((char*)(this) - (((char*)&((Node*)(0xffff))->visitCount) - (char*)(0xffff))))->get_visitCount(); }
    void operator=(int v) { ((Node*)((char*)(this) - (((char*)&((Node*)(0xffff))->visitCount) - (char*)(0xffff))))->set_visitCount(v); }    
    private: char zero[0];
    } visitCount;
};  

我的想法是我也会添加我自己的实现:

int get_visitCount();
void set_visitCount(int v); 

它看起来好像visitCount被直接访问了。
但是,这些函数实际上会在幕后调用:

Node n;
n.visitCount = 1;     //actually calls set method
cout << n.VisitCount; //actually calls get method  

我想更多地了解这个访问封闭类的技巧:

((Node*)((char*)(this) - (((char*)&((Node*)(0xffff))

的相关性是0xffff什么?
十进制即: 65535.

这如何欺骗编译器访问包含 visitCount 类的类?

我还看到这在 MSVC 上不起作用,所以我想知道是否有标准的方法来完成这个黑客正在做的事情。

4

1 回答 1

3

没有相关性0xffff。这只是一些数字。它可能是零(如果是的话,实际上会更容易)。让我们将其分解并重写0xffffaddr

(((char*)&((type*)(addr))->name) - (char*)(addr))

(type*)(addr)只是给我们一些type*addr. 这是一个reinterpret_cast。所以让我们称之为obj

 (((char*)&obj->name) - (char*)(addr))

我们甚至可以将第二个替换为addr-obj它是错误的类型,但无论如何我们都在强制转换,它增加了正在发生的事情的清晰度:

 (((char*)&obj->name) - (char*)(obj))

&obj->name只是给了我们一个指向那个特定成员的指针,所以我们称之为mem_ptr

((char*)mem_ptr) - (char*)(obj)

现在很清楚了——我们将成员的地址(作为 a char*)减去父对象的地址(作为 a char*)。我们过去0xffff只是在两个地方都有相同的初始地址。

请注意,C++ 标准也直接为此定义了一个宏。它被称为offsetof。需要注意的是“如果type不是标准布局类(第 9 条),则结果是未定义的。”

于 2015-05-26T03:28:37.417 回答