为什么COM里的QueryInterface第二个参数需要使用双重指针?

转载自:http://www.cnblogs.com/perhaps/archive/2004/09/08/41143.html

昨天向COM的世界迈出了第一步,虽然没有想象中那样举步维艰,却也走得不轻松。在初步了解DirectX的基础——COM之后,再次将注意力转到DirectX上来。正如一篇介绍COM的文章所说的,使用COM远比了解COM要简单得多。确实,在DirectX中使用COM都是那样的自然,跟一般函数的调用并无二致。但是,使用起来很自然还是没有阻止自己脑袋瓜中不断涌出的问号。大家看以下代码:

LPDIRECTDRAW lpdd =  NULL;
if( FAILED(lpdd->QueryInterface(IID_IDirectDraw7, (LPVOID *&
lpdd)))
    
{
          
//error handle and return

    }

代码很简单,就是调用了QueryInterface方法去获得DirectDraw7的接口指针。然而我第一眼看过去的时候,总是觉得有点别扭。大家再看一下,我昨天实现第一个COM对象的时候override的QueryInterface方法:

HRESULT __stdcall ComObject::QueryInterface(const IID &iid, void **  iface)
{
    
if(iid ==
 IID_IX)
       
*iface = (InterfaceX*this
;
    
else
 
        
if (iid ==
 IID_IY)
            
*iface = (InterfaceY*this
;
    
else

        
*iface = 0;
        
return
 E_NOINTERFACE;
        ((IUnknown
*) (*iface))->
AddRef();
    
return
 (S_OK);
}


        我觉得别扭是因为第二个参数少了个dereference操作符,尽管LPVOID就是void *,但是通过使用typedef关键字
将其定义为LPVOID,确实让代码可读性更高了。这是我想到第一个问题;
        接着就是一个感觉很低级的问题了:QueryInterface为什么要使用指针的指针作为参数呢?直接用指针不行吗?
如果仅仅是为了在函数体中能够改变参数,使用指针就足够了。那是因为什么呢?我们从如下形式的QueryInterface方法来看:

HRESULT QueryInterface( REFIID iid, void **  ppvObject);
HRESULT QueryInterface( REFIID riid,LPVOID 
*
ppvObj);

        第一,ppvObject这个参数的含义是接口指针的地址(Address of a pointer to fill with the interface pointer)。从这个角度而言,第二种函数形式就更为容易理解了,由于"*"为解引用操作符,作用于ppvObject,得到的类型正好就是一个接口指针了(与LPVOID相对应);
        第二,QueryInterface如果执行成功,就会将相应的接口指针赋给参数*ppvObject,如果仅仅是传递接口指针本
身,而非接口指针的地址,就无法在函数体中对其进行赋值了。大家看以下代码:

class  X
{
public
:
 
virtual void
 sayHi(); 
}
;

class Y:public
 X
{
public
:
 
void
 sayHi();
}
;

                                                                                    (Test.hpp)

#include <iostream>
#include 
"Test.hpp"

using namespace  std;

void
 X::sayHi()
{
 cout 
<< "Hi, My name is X." <<
 endl;
}


void  Y::sayHi()
{
 cout 
<< "Hi, My name is Y." <<
 endl;
}


void change(X * x)
{
  x 
= new
 Y;
}


void  main()
{
 X
* a = new
 X;
 a
->
sayHi();
 change(a);
 a
->
sayHi();
}


        大家会觉得输出是什么呢?我们所期待的Y给大家的问候语并没有出现,就剩下X在唱独角戏。不管如何折腾,Y就是不会出现。如果使用了指针的指针,我们就可以得到体现多态的结果了。
        PS:其实这是一个很简单的问题,但是通过摸索让自己对双重指针有了进一步的认识,以前就知道双重指针的定义就是指针的指针,至于它为什么会存在,一点想法都没有。在瞎胡闹的过程中,同时还强化了一个概念,就是运算符的优先级,类型转化的优先级要比"->"和"."要低。
        我还是C++方面的菜鸟,各位高手看了,笑笑就好了,别笑太大声哦。 :)

`QueryInterface` 是 COMComponent Object Model)接口模型中用于实现多态的关键函数。它在 `IUnknown` 接口中定义,其他接口通常会继承这个功能。这个函数的作用是让一个对象能够查询并返回其支持的指定接口。 参数详解如下: 1. **REFIID** `riid`: 这是请求的接口标识符(Interface Identifier),通过它可以识别特定的接口。当你想要从一个对象动态地获取另一个接口时,你需要传递该对象能提供哪个接口的 `IID`。比如,在D3D中,虽然不需要直接指定 `clsid`,但通过 `IID` 可以询问对象是否实现了 `ID3D11Device` 或者 `ID3D11ShaderResourceView` 等。 2. **void** `**ppvObject`: 一个指向 `void` 的指针数组,如果 `QueryInterface` 成功,这会被设置为请求接口的实际指针。`**` 表示这是一个可变参数,允许函数修改它的值。 具体实现时,如引用[2]所示: ```cpp HRESULT QueryInterface(REFIID riid, void **ppvObject) { // 检查 riid 是否匹配已知的接口 if (riid == IID_ICar || riid == IID_IUnknown) // 示例中的 ICar 和 IUnknown 类型 { // 创建并返回相应的接口实例 if (riid == IID_ICar) *ppvObject = static_cast<ICar*>(this); else *ppvObject = static_cast<IUnknown*>(this); // 返回成功状态 return S_OK; } else { // 不支持此接口,返回 E_NOTIMPL 错误 return E_NOTIMPL; } } ``` 如果你想要调用 `QueryInterface` 来获得一个对象的某个特定接口,你需要先知道你要的那个接口的 `IID`,然后将其传递给 `QueryInterface` 函数。如果对象支持该接口,函数会返回成功,并提供所需的接口指针;反之则返回不支持的错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值