【问题标题】:why it use void** here?为什么它在这里使用 void**?
【发布时间】:2017-01-07 12:42:46
【问题描述】:

v8-0.2.5获取的代码

/**
 * Checks whether two handles are the same.
 * Returns true if both are empty, or if the objects
 * to which they refer are identical.
 * The handles' references are not checked.
 */
template <class S> bool operator==(Handle<S> that) {
  void** a = reinterpret_cast<void**>(**this);
  void** b = reinterpret_cast<void**>(*that);
  if (a == 0) return b == 0;
  if (b == 0) return false;
  return *a == *b;
}  

Handle 重载运算符* 以便 **this*that 返回类型 T*

好像是这样

  void* a = reinterpret_cast<void*>(**this);
  void* b = reinterpret_cast<void*>(*that);
  return a == b;

也会很好用吗?

【问题讨论】:

  • 这种魔法过去是在旧的 C 代码中完成的,通过 void* 类型的引用变量传递。我认为,如果您将 return 更改为 a == b 并具有 b 以及 void* 类型的 be ,它将运行良好。
  • 如果取消引用 Handle&lt;T&gt; 会产生 T*,那么这段代码就有很大的缺陷,因为比较会产生误报和漏报:如果 sizeof(T) &lt; sizeof(void*) 你会得到误报,因为这样做的东西不属于的对象包含在比较中,如果sizeof(T) &gt; sizeof(void*) 会得到误报,因为只比较对象的前四个/八个字节。

标签: c++ v8 reinterpret-cast


【解决方案1】:

如果 ab 具有 void* 类型,那么您不能取消引用它们(不先将它们转换为其他东西),因此 *a == *b 将不起作用。

【讨论】:

  • 但是你不能只用 a == b 吗?
  • 不,你不能,因为在文档评论中它说objects are identical,这意味着它们不必是相同的对象才能返回true,2个不同的对象仍然可以比较相等因为它们是相同的。
【解决方案2】:

首先,我承认我只是对角阅读链接中的代码。显然,Handle 类重载了取消引用运算符 (*) 以返回正在处理的 T*。因此,第一行中的表达式含义如下:

  • this 是(可能具有 cv 资格)Handle&lt;T&gt; * const
  • *thisHandle&lt;T&gt; &amp;
  • **this是句柄的operator*的返回值,也就是你说的T*
  • 最后,T* 被重新解释为void**。请注意,添加了一个额外的间接,因此可以取消引用结果并将产生 void* &amp; 而不是 T&amp;
  • that 等效的行产生一个S*,它被重新解释为void**

因此,您会得到几个指向不同类型 T*S* 的指针,它们被神奇地重新解释为 void**。然后代码执行空检查和然后,魔术线:

return *a == *b;

比较TS 类型对象的(可能未对齐!)第一个sizeof(void*) 字节实际上 指向@ 987654344@ 和b。除非您可以完全确定 TS 具有正确的大小和对齐方式,否则该检查完全是假的。 例如,如果您知道T 本身始终是具有相同大小的指针的指针或智能指针对象,则检查将是有意义的,因此您可能有不同的句柄指向不同的指针对象尽管如此(在第二间接级别)指向同一个对象。这允许 GC 移动底层对象,而无需更新它的所有句柄,只需更新 1 级指针的内容。

Handle<T> has T* -----> T = U* pinned in memory -----> actual object U can be moved

因此,为了回答您的问题,仅转换为 void*(不增加间接性)与在代码中进行检查不同 - 您的版本会比较指针,所以在在我的示例中,同一对象的两个不同句柄可能与您的备用代码比较不相等。

PS:让你的班级从operator*operator-&gt; 返回T* 也是不好的风格,因为那样你就打破了p-&gt;x(*p).x 之间的一般身份。如果成员访问运算符返回 T*,则取消引用运算符通常应返回 T&amp;

【讨论】:

  • 查看 V8 代码后,这是一个正确的分析。在我看来,所呈现的代码具有 - 取决于所处理的类型 - 未定义的行为,因为任意类型的对象被作为指针访问(这是未定义的原因是某些架构可能会在其地址寄存器中加载无效位模式时陷入困境) .正确的方法是将两个T* 值都转换为void *,然后进行数字比较;那么TS 的字节可以在将T* 都转换为char 指针后进行比较,这是一个允许的异常。
  • 字节比较是可能的,因为 sizeof(T)sizeof(S) 是已知的模板(但在示例中被忽略;事实上,如果基础类型小于地址(可能是 8 个字节!) ,目前的比较比较的是不相关的内存部分,在逻辑上也是错误的)。
【解决方案3】:

Javier Martín 是对的。你不能像你所提出的问题那样只比较指针。首先在 ov v8 的上下文中,Handle&lt;T&gt;T 类型有限制。您不能参加任何课程并将 Handle 应用于它。 T 只是用户处理的外观类:v8::Stringv8::Integer 等等。这种类型的对象永远不会被创建,但这些类被用作内部的接口。

实际上Handle&lt;&gt; 存储的指针是指向某事物的指针,比如说“标签”。如果两个Handle&lt;&gt; 的标签相同,我们就有相同的对象。内部标签的大小为void*,并且以某种方式引用了真实对象。用户不需要知道标签是什么,所以Handle&lt;&gt; 使用void* 代替。一些想法

  1. Handle&lt;T&gt;T* --> 标记(不是T)--(不知何故)--> 真实对象(不是 T 型)

  2. 标签的大小为void*。而且用户不需要知道Tag的真实类型。

  3. 两个标签相等——它们指向同一个对象。

  4. (总结,从用户的角度来看)Handle&lt;T&gt; 拥有void** --> void*

所以原来的bool operator==(Handle&lt;S&gt; that) 正在做应该做的事情:比较标签的值。但首先检查指针。

【讨论】:

    猜你喜欢
    • 2022-06-16
    • 1970-01-01
    • 2014-06-06
    • 1970-01-01
    • 2018-12-10
    • 2022-12-19
    • 1970-01-01
    • 2021-06-14
    • 1970-01-01
    相关资源
    最近更新 更多