当我在COM中开发时,我总是看到(void **)类型转换如下。
QueryInterface(/* [in] */ REFIID riid,/* [out] */ void** ppInterface)
它的确切含义是什么?
恕我直言,它告诉编译器不要强制执行类型验证,因为在编译时客户端代码不知道ppInterface指向的类型。
感谢~~~
我这样理解:
void * p 暗示 AnyType * p
void ** pp 暗示 指向AnyType *的指针
如果void ** pp表示“指向void *的指针”,那么编译器在看到它时会做什么检查?
答案 0 :(得分:18)
COM将void**
与QueryInterface
一起使用的原因有点特殊。 (见下文。)
通常, void**
只是指向void*
的指针,它可以用于out参数,即。指示函数可以返回值的位置的参数。您的评论/* [out] */
表示ppvInterface
指向的位置将被写入。
“为什么带有指针类型的参数可以用作输出参数?”,你问?请记住,您可以使用指针变量更改两件事:
ptr = ...
)*ptr = ...
)指针按值传递给函数,即。该函数获取自己传递给它的原始指针的本地副本。这意味着您可以更改函数(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();
}