void**的含义是什么?(涉及IT技术)

11

当我在COM开发时,我总是看到以下形式的(void**)类型转换。

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

它的确切含义是什么?

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

谢谢~~~

更新1

我这样理解:

void* p 意味着 AnyType* p

void ** pp 意味着 指向 AnyType* 的指针

更新2

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


4
你的更新有误 - 第一部分是正确的,但第二部分不正确。 void **pp 只意味着 "指向 void * 的指针"。 - caf
谢谢caf。有点难以理解... - smwikipedia
如果void*pp表示“指向void的指针”,那么编译器在看到它时会进行哪些检查? - smwikipedia
据我所知没有。 - Seamus Connor
9个回答

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

17
COM在使用QueryInterface时为什么要使用void**类型有其特殊原因(见下文)。一般情况下,void**表示指向void*的指针,可用于输出参数,即函数返回值的位置。您的注释/* [out] */表示将写入由ppvInterface指向的位置。
你问: "为什么指针类型的参数可以用作输出参数?" 请记住,指针变量可以更改两个内容:
1. 您可以更改指针本身,使其指向另一个对象。 (ptr = ...) 2. 您可以修改指向的对象。(*ptr = ...)
指针按值传递给函数,即函数获得其自己的原始指针的本地副本。这意味着您可以更改函数内部的指针参数(1),而不影响原始指针,因为只有本地副本会被修改。但是,您可以更改指向的对象(2),并且这将在函数外部可见,因为副本具有与原始指针相同的值,因此引用相同的对象。
现在来看COM的情况: 1. 接口指针(由riid指定)将在由ppvInterface引用的变量中返回。QueryInterface通过上述的机制(2)来实现这一点。 2. 使用void**,需要一个* 来允许机制(2); 另外一个* 反映了QueryInterface并不返回新创建的对象(IUnknown),而是返回已经存在的对象: 为了避免该对象的复制,将返回指向该对象(IUnknown*)的指针。 3. 如果您想知道为什么ppvInterface具有void**类型,而不是更合理的类型安全性别,例如IUnknown**(因为所有接口必须派生自IUnknown),那么请阅读以下论据,摘自Don Box的书《Essential COM》第60页(章节):

One additional subtlety related to QueryInterface concerns its second parameter, which is of type void **. It is very ironic that QueryInterface, the underpinning of the COM type system, has a fairly type-unsafe prototype in C++ [...]

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

Unfortunately, the following looks equally correct to the C++ compiler:

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

This more subtle variation also compiles correctly:

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

Given that the rules of inheritance do not apply to pointers, this alternative definition of QueryInterface does not alleviate the problem:

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

The same limitation applies to references as to pointers as well. The following alternative definition is arguably more convenient for clients to use:

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

[...] Unfortunately, this solution does not reduce the number of errors [...] and, by eliminating the need for a cast, removes a visual indicator that C++ type safety might be in jeopardy. Given the desired semantics of QueryInterface, the argument types Microsoft chose are reasonable, if not type safe or elegant. [...]


有争议的是,实际上应该是 void *pv; hr = punk->QueryInterface(IID_ICAT, &pv); pPug = pv;(指的是书中引用的段落)。 - caf

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

1
该语言不允许您在没有显式转换的情况下将 Something ** 传递给期望 void ** 的函数。此外,整个方法是无效的。COM 中使用的技巧基于实现细节,通常不可移植。 - AnT stands with Russia
1
@AndreyT:这是C++语言,但C语言也能胜任。据我所知,使用不安全模式的C#语言也可以胜任。 - leppie
2
@leppie:C 可能只是因为许多 C 编译器在这种情况下仅限于发出“警告”而感到“高兴”。但即使在 C 中,这仍然是一种约束违规(即“错误”)。 - AnT stands with Russia
@jamesdlin:谢谢,尽管我不完全同意他们的“可移植”示例。将dp分配给vp没有任何目的,因为在调用f之后,vp将被覆盖。 - leppie
@leppie:不,我是在说相反的意思。char *void *明确保证具有相同的大小、对齐要求和表示方式;但是对于其他指针类型,没有提供同样的保证(还有一些其他的保证,比如所有指向struct类型的指针具有相同的大小、对齐和表示方式)。 - caf
显示剩余9条评论

1

通过使用void*,还可以确保指向的对象不会被意外删除或篡改。

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


0

不要使用指向指针的指针,尝试使用指向指针的引用。这比使用 ** 更符合 C++ 的风格。

例如:

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

0

0

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

当返回多种可能类型时,没有共同的超类型(例如QueryInterface),返回void*确实是唯一的选择,并且由于这需要作为[out]参数传递,因此需要该类型的指针(void**)。


0
不强制执行类型验证。
实际上,void*void**的存在是为了允许使用不同类型的指针,这些指针可以向下转换为void*以适应函数参数类型。

0

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接