【问题标题】:what's the implication of void**?void** 的含义是什么?
【发布时间】:2011-03-30 19:48:20
【问题描述】:

当我在 COM 中开发时,我总是看到 (void**) 类型转换如下。

QueryInterface(/* [in] */ REFIID riid,/* [out] */ void** ppInterface)

它的确切含义是什么?

恕我直言,它告诉编译器不要强制类型验证,因为 ppInterface 指向的类型在编译时客户端代码不知道。

谢谢~~~

更新 1

我是这样理解的:

void* p 暗示 AnyType* p

void ** pp 暗示 指向 AnyType*

的指针

更新 2

如果 void**pp 表示“指向 void* 的指针”,那么编译器在看到它时会进行哪些检查?

【问题讨论】:

  • 您的更新是错误的 - 第一部分是正确的,但第二部分不是。 void **pp 仅表示“指向void * 的指针”。
  • 谢谢咖啡馆。有点难以理解...
  • 如果 void*pp 表示“指向 void 的指针”,那么编译器在看到它时会进行哪些检查?
  • 我不知道。

标签: c# c++ c com


【解决方案1】:

COM 使用void**QueryInterface 的原因有些特殊。 (见下文。)

一般来说, void** 就是指向void* 的指针,可以用于out 参数,即。指示函数可以返回值的位置的参数。您的评论/* [out] */ 表示将写入ppvInterface 指向的位置。

“为什么可以使用指针类型的参数作为输出参数?”,你问?请记住,您可以使用指针变量更改两件事:

  1. 您可以更改指针本身,使其指向另一个对象。 (ptr = ...)
  2. 您可以修改指向的对象。 (*ptr = ...)

指针按值传递给函数,即。该函数获取传递给它的原始指针的本地副本。这意味着您可以在函数 (1) 中更改指针参数而不影响原始指针,因为仅修改了本地副本。但是,您可以更改指向的对象 (2),这将在函数外部可见,因为副本与原始指针具有相同的值,因此引用了相同的对象。

现在,特别是关于 COM:

  • 将在ppvInterface 引用的变量中返回指向接口(由riid 指定)的指针。 QueryInterface 是通过上述机制(2)实现的。

  • 对于void**,需要一个*来允许机制(2);另一个*反映了QueryInterface不返回一个新创建的对象(IUnknown),而是一个已经存在的对象:为了避免该对象的重复,指向该对象的指针(IUnknown*)被退回。

  • 1234563以下论点取自 Don Box 的书Essential COM,p。 60(Type Coercion and IUnknown章节):

QueryInterface 相关的另一个微妙之处在于它的第二个参数,它的类型为void **。具有讽刺意味的是,COM 类型系统的基础QueryInterface 在 C++ 中有一个相当不安全的原型 [...]

 IPug *pPug = 0;
 hr = punk->QueryInterface(IID_IPug, (void**)&pPug);

不幸的是,对于 C++ 编译器,以下内容看起来同样正确:

 IPug *pPug = 0;
 hr = punk->QueryInterface(IID_ICat, (void**)&pPug);

这种更细微的变化也能正确编译:

 IPug *pPug = 0;
 hr = punk->QueryInterface(IID_ICat, (void**)pPug);

鉴于继承规则不适用于指针,QueryInterface 的这种替代定义并不能缓解问题:

 HRESULT QueryInterface(REFIID riid, IUnknown** ppv);

同样的限制也适用于指针的引用。以下替代定义可以说更方便客户使用:

 HRESULT QueryInterface(const IID& riid, void* ppv);

[...] 不幸的是,这个解决方案并没有减少错误的数量[...],并且,通过消除强制转换的需要,消除了 C++ 类型安全性可能存在的视觉指示在危险之中。鉴于 QueryInterface 的所需语义,Microsoft 选择的参数类型是合理的,即使不是类型安全或优雅的。 [...]

【讨论】:

  • 按理说,其实应该是void *pv; hr = punk->QueryInterface(IID_ICAT, &pv); pPug = pv;(参考书中引用的一段话)。
【解决方案2】:

尝试使用对指针的引用,而不是使用指向指针的指针。它比使用 ** 多一点 C++。

例如

void Initialise(MyType &*pType)
{
    pType = new MyType();
}

【讨论】:

    【解决方案3】:

    它只是一个指向void*的指针。

    例如:

    Something* foo;
    Bar((void**)&foo);
    
    // now foo points to something meaningful
    

    编辑:一个可能的 C# 实现。

      struct Foo { }
    
      static Foo foo = new Foo();
    
      unsafe static void Main(string[] args)
      {
        Foo* foo;
    
        Bar((void**)&foo);
      }
    
      static unsafe void Bar(void** v)
      {
        fixed (Foo* f = &foo)
        {
          *v = f;
        }
      }
    

    【讨论】:

    • 该语言不允许您在没有显式转换的情况下传递Something **,而void **。而且,整个方法是无效的。 COM 中使用的技巧是基于实现细节的,这些细节通常是不可移植的。
    • @AndreyT:在 C++ 中,但 C 会很高兴。处于不安全模式的 IIRC C# 也会很高兴。
    • 不,甚至在 C 中也没有。Anything * 可以隐式地与void * 相互转换,但对于Anything **void **不是也是如此。仅当 void ** 指向实际的 void * 对象(或 char *,由于特殊例外)时,才应取消引用。
    • @leppie:C 可能是“快乐的”,只是因为许多 C 编译器在这种情况下将自己限制为仅仅是“警告”。尽管如此,即使在 C 中,这仍然是违反约束(即“错误”)。
    • @AndreyT:虽然我相信你说的是真的,但我还是不明白为什么。也许我会尝试就我的确切“问题”提出一个问题:) 谢谢
    【解决方案4】:

    void ** 是指向void * 的指针。这可用于传递将用作输出参数的void * 变量的地址 - 例如:

    void alloc_two(int n, void **a, void **b)
    {
        *a = malloc(n * 100);
        *b = malloc(n * 200);
    }
    
    /* ... */
    
    void *x;
    void *y;
    
    alloc_two(10, &x, &y);
    

    【讨论】:

      【解决方案5】:

      通过 void * 传递也确保指向的对象不会被删除或篡改(意外)。

      “这意味着不能使用 void* 类型的指针删除对象,因为没有 void 类型的对象。”

      【讨论】:

        【解决方案6】:

        指向可以提供的未知接口的指针。

        【讨论】:

          【解决方案7】:

          不强制类型验证

          确实,void*void** 允许使用不同类型的指针,可以向下转换为 void* 以适应函数参数类型。

          【讨论】:

            【解决方案8】:

            它允许 API 指定将来可以将指针用作 [in-out] 参数,但目前,该指针未使用。 (NULL 通常是必需的值。)

            当返回许多可能的类型之一,没有公共超类型(例如使用 QueryInterface)时,返回 void* 确实是唯一的选择,因为这需要作为 [out] 参数传递一个指向该类型的指针(void**) 是必需的。

            【讨论】:

              【解决方案9】:

              它是指向您使用此调用请求的接口指针的指针。显然你可以请求各种接口,所以它必须是一个空指针。如果接口不存在,则指针设置为 NULL。

              编辑:可在此处找到详细信息:http://msdn.microsoft.com/en-us/library/ms682521(VS.85).aspx

              【讨论】:

                猜你喜欢
                • 2013-06-28
                • 1970-01-01
                • 1970-01-01
                • 2014-10-13
                • 2017-12-09
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多