虚空**的含义是什么?

时间:2010-08-17 06:58:22

标签: c# c++ c com

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

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

它的确切含义是什么?

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

感谢~~~

更新1

我这样理解:

void * p 暗示 AnyType * p

void ** pp 暗示 指向AnyType *的指针

更新2

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

9 个答案:

答案 0 :(得分:18)

COM将void**QueryInterface一起使用的原因有点特殊。 (见下文。)

通常, void**只是指向void*的指针,它可以用于out参数,即。指示函数可以返回值的位置的参数。您的评论/* [out] */表示ppvInterface指向的位置将被写入。

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

  1. 您可以更改指针本身,使其指向另一个对象。 (ptr = ...
  2. 您可以修改指向对象。 (*ptr = ...
  3. 指针按值传递给函数,即。该函数获取自己传递给它的原始指针的本地副本。这意味着您可以更改函数(1)中的指针参数,而不会影响原始指针,因为只修改了本地副本。但是,您可以更改指向对象(2),这将在函数外部可见,因为副本具有与原始指针相同的值,因此引用相同的对象。

    现在,特别关注COM:

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

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

    • 返回对象(IUnknown*
    • 如果您问为什么ppvInterface类型void**而不是IUnknown**,这似乎更合理的类型安全性(因为所有接口必须来自{ {1}}),然后阅读Don Box的书 Essential COM 中的以下参数,p。 60(章节类型强制和IUnknown ):


      

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

    QueryInterface
         

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

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

    这种更微妙的变化也可以正确编译:

     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);
    
         

    [...]不幸的是,这个解决方案不会减少错误数量,而且通过消除对强制转换的需要,删除了C ++类型安全可能的可视指示符在危险之中。给定 HRESULT QueryInterface(const IID& riid, void* ppv); 所需的语义,微软选择的参数类型是合理的,如果不是类型安全或优雅的话。 [...]

答案 1 :(得分:17)

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);

答案 2 :(得分:5)

它只是指向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;
    }
  }

答案 3 :(得分:1)

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

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

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

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

当返回许多可能类型中的一个,没有常见的超类型(例如使用QueryInterface)时,返回void *实际上是唯一的选项,因为这需要作为[out]参数传递给该类型的指针(void **)是必要的。

答案 6 :(得分:0)

  

不强制执行类型验证

实际上,void*void**允许使用不同类型的指针,这些指针可以下载到void*以适合函数参数类型。

答案 7 :(得分:0)

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

答案 8 :(得分:0)

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

e.g。

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