空指针指针(void **)

Tri*_*ung 4 c++ com pointers void-pointers pointer-to-pointer

我正在http://msdn.microsoft.com/zh-cn/library/windows/desktop/dd389098(v=vs.85).aspx上阅读COM示例。

我真的无法理解(void **)

hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
Run Code Online (Sandbox Code Playgroud)

所以我尝试了由类的不同类型的指针返回的一些值

class Point{
private:
    int x, y;
public:
    Point(int inputX, int inputY){x = inputX, y = inputY;}
    int getX(){return x;}
    int getY(){return y;}
    friend ostream& operator << (ostream &out, Point &cPoint);
    Point operator-(){
        return Point(-x, -y);
    }
};

ostream& operator << (ostream &out, Point &cPoint){
    return out<< "(" << cPoint.x << ", " << cPoint.y << ")";
}
Run Code Online (Sandbox Code Playgroud)

然后打印出来

Point *p = new Point(1,2);
cout << p << endl << &p << endl << endl
<< *&p << endl<< **&p << endl<<endl 
<< (void *) &p << endl << (void **) &p ;
Run Code Online (Sandbox Code Playgroud)

(void *)与(void **)确实没有区别。(void **)&pControl要返回什么?

Mr.*_*C64 5

hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
Run Code Online (Sandbox Code Playgroud)

什么是(void **)&pControl想返回?

QueryInterface()是的三种方法之一IUnknown,它是所有 COM接口的基本根接口。

在为MSDN文档IUnknown::QueryInterface()中明确指出:

HRESULT QueryInterface(
  [in]   REFIID riid,
  [out]  void **ppvObject
);
Run Code Online (Sandbox Code Playgroud)

ppvObject [out]指针变量的地址,该变量接收riid参数中请求的接口指针。成功返回后,* ppvObject包含所请求的指向对象的接口指针。如果对象不支持该接口,则将* ppvObject设置为NULL。

因此,在特定情况下,成功返回后,pControl将包含请求的指向IMediaControl接口的指针,如通过第一个参数在函数调用中指定的那样IID_IMediaControl。


现在,让我们尝试更好地理解为什么使用双指针间接寻址:void**。

void*表示“指向任何东西的指针”。

因此,人们可能会想:“为什么不QueryInterface()只是一个第二个参数void*?”

问题在于此参数是输出参数。这意味着QueryInterface()将在该参数中写入一些内容,以供调用者使用。

并且,在C语言中(COM具有多种C语言),当您具有输出参数时,必须使用指针(*)。
(请注意,在C ++中,您也可以使用参考&。)

因此,在这种情况下,我们具有第一个间接层void*,即“指向任何内容的指针”。
第二个间接级别(另一个*),意味着:“这是一个输出参数”。

您也可以通过以下方式考虑它:

typedef void* PointerToAnything;

HRESULT QueryInterface(..., /* [out] */ PointerToAnything* pSomeInterface);

// pSomeInterface is an output parameter.
//
// [out] --> use * (pointer), 
// so it's 'PointerToAnything*' (not just 'PointerToAnything'),
// so, with proper substitution, it's 'void**' (not just 'void*').
Run Code Online (Sandbox Code Playgroud)