C++ COM对象释放函数在传递一个;IUnknown*&&引用;作为参数

C++ COM对象释放函数在传递一个;IUnknown*&&引用;作为参数,c++,pointers,com,pass-by-reference,directx-9,C++,Pointers,Com,Pass By Reference,Directx 9,在标题中,包含以下代码 inline void SafeRelease( IUnknown * & in_COM_Pointer ) { if ( NULL != in_COM_Pointer ) { in_COM_Pointer->Release(); in_COM_Pointer = NULL; } } 当按如下所示使用时 SafeRelease(_D3DDevic

在标题中,包含以下代码

inline void SafeRelease( IUnknown * & in_COM_Pointer )
    {
        if ( NULL != in_COM_Pointer )
        {
            in_COM_Pointer->Release();
            in_COM_Pointer = NULL;
        }
    }
当按如下所示使用时

SafeRelease(_D3DDevice); // _D3DDevice is a "IDirect3DDevice9 *". According to documentation it is inherited from "IUnknown".
它给出了一个编译错误:

错误C2664:“安全释放”:无法从转换参数1 “IDirect3DDevice9*”到“IUnknown*&”

我知道如何使用模板或宏编写此函数。但我想知道为什么会这样

  • 为什么这会产生错误
  • 如何正确书写
  • 如果继承不能用于编写此函数,我应该使用模板吗
  • 在使用建议的方法实现它时,我需要注意什么
  • 注意:要完全掌握本帖中的信息,需要了解以下方面的知识


    介绍 在这里,我们尝试将
    \u D3DDevice
    的地址传递给SetRelease,但它不可调用,因为它需要指向IUnknown的指针类型的左值(引用)

    仅仅因为派生继承自Base,并不意味着指向派生的左值指针可以转换为指向Base的左值指针类型

    Derived*
    Base*
    的隐式转换将产生右值

    struct A     { };
    struct B : A { };
    
    void func (A*&);
    


    “解决方案”是什么?
    SafeRelease(&_D3DDevice);//(A)
    
    (A) 将生成指向IDirect3DDevice9的临时类型指针,此指针可以隐式转换为指向IUnknown的指针,因为IDirect3DDevice9继承自IUnknown

    我们不再试图形成对隐式生成指针的左值引用,代码将编译


    启示 。。。但是,这也意味着我们将无法更新传入的任何指针参数的值,因此,如果这是一项要求,那么您必须/应该使用模板,以便可以获得作为参数传递的实际值的引用

    template<typename T>
    inline void SafeRelease(T * & in_COM_Pointer);
    
    模板
    内联无效安全释放(T*&in_COM_指针);
    
    注意:要完全掌握本帖中的信息,需要了解以下方面的知识


    介绍 在这里,我们尝试将
    \u D3DDevice
    的地址传递给SetRelease,但它不可调用,因为它需要指向IUnknown的指针类型的左值(引用)

    仅仅因为派生继承自Base,并不意味着指向派生的左值指针可以转换为指向Base的左值指针类型

    Derived*
    Base*
    的隐式转换将产生右值

    struct A     { };
    struct B : A { };
    
    void func (A*&);
    


    “解决方案”是什么?
    SafeRelease(&_D3DDevice);//(A)
    
    (A) 将生成指向IDirect3DDevice9的临时类型指针,此指针可以隐式转换为指向IUnknown的指针,因为IDirect3DDevice9继承自IUnknown

    我们不再试图形成对隐式生成指针的左值引用,代码将编译


    启示 。。。但是,这也意味着我们将无法更新传入的任何指针参数的值,因此,如果这是一项要求,那么您必须/应该使用模板,以便可以获得作为参数传递的实际值的引用

    template<typename T>
    inline void SafeRelease(T * & in_COM_Pointer);
    
    模板
    内联无效安全释放(T*&in_COM_指针);
    
    IDirect3DDevice9
    继承自
    IUnknown
    ,但它并不完全是
    IUnknown
    。这使得
    IDirect3DDevice9*
    变量与
    IUnknown*&
    参数不兼容

    template<typename T>
    inline void SafeRelease(T * & in_COM_Pointer);
    
    因此,您需要在类型之间转换,或者使用更灵活的基于模板的释放功能,例如:

    template <typename IFoo>
    VOID SafeRelease(IFoo*& pFoo)
    {
        if(!pFoo)
            return;
        pFoo->Release();
        pFoo = NULL;
    }
    
    IDirect3DDevice9* pDevice = ...
    ...
    SafeRelease(pDevice);
    

    IDirect3DDevice9
    继承自
    IUnknown
    ,但它并不完全是
    IUnknown
    。这使得
    IDirect3DDevice9*
    变量与
    IUnknown*&
    参数不兼容

    template<typename T>
    inline void SafeRelease(T * & in_COM_Pointer);
    
    因此,您需要在类型之间转换,或者使用更灵活的基于模板的释放功能,例如:

    template <typename IFoo>
    VOID SafeRelease(IFoo*& pFoo)
    {
        if(!pFoo)
            return;
        pFoo->Release();
        pFoo = NULL;
    }
    
    IDirect3DDevice9* pDevice = ...
    ...
    SafeRelease(pDevice);
    

    COM不允许转换接口指针,您必须使用QueryInterface()。这是由C++编译器强制执行的。像这样:

    class Base {};
    class Derived : /*public*/ Base {};
    
    inline void SafeRelease(Base* & ptr) {}
    
    void test() {
        auto p = new Derived;
        SafeRelease(p);      // C2664
    }
    
    您可以使用模板功能执行此操作:

    template<typename T>
    inline void SafeRelease(T * & in_COM_Pointer) {
        if (NULL != in_COM_Pointer) {
            in_COM_Pointer->Release();
            in_COM_Pointer = NULL;
        }
    }
    
    模板
    内联无效安全释放(T*&in_COM_指针){
    if(NULL!=in_COM_指针){
    在_COM_指针->Release()中;
    in_COM_指针=NULL;
    }
    }
    
    COM不允许强制转换接口指针,您必须使用QueryInterface()。这是由C++编译器强制执行的。像这样:

    class Base {};
    class Derived : /*public*/ Base {};
    
    inline void SafeRelease(Base* & ptr) {}
    
    void test() {
        auto p = new Derived;
        SafeRelease(p);      // C2664
    }
    
    您可以使用模板功能执行此操作:

    template<typename T>
    inline void SafeRelease(T * & in_COM_Pointer) {
        if (NULL != in_COM_Pointer) {
            in_COM_Pointer->Release();
            in_COM_Pointer = NULL;
        }
    }
    
    模板
    内联无效安全释放(T*&in_COM_指针){
    if(NULL!=in_COM_指针){
    在_COM_指针->Release()中;
    in_COM_指针=NULL;
    }
    }
    
    “1.为什么会出现错误

    考虑这一点:

    struct Animal {};
    struct Dog: Animal { void bark() {} };
    struct Dolphin: Animal { void dive() {} };
    
    void foo( Animal*& p ) { p = new Dolphin(); }
    
    auto main() -> int
    {
        Dog* p = new Dog();
        foo( p );             //! C2664, Would have changed p to point to Dolphin.
        p->bark();            // Uh huh...
    }
    
    所以,这是不允许的

    还有更多相同之处,例如,关于实际参数与形式参数的深度
    常数,它通常被称为Liskov替换原则,即继Barbara Liskov之后的LSP


    “2.如何正确书写

    一般问题的一个解决方案是使用模板直接处理手头的一个或多个类型,而不进行转换

    然而,在这个具体的例子中,只要您确定没有空指针,只需调用
    p->Release()
    ,而不是
    SafeRelease(p)


    “3.如果继承不能用于编写此函数,我应该使用模板吗

    你可以使用模板,但这不是必须的;见上文


    “4.在使用建议的方法实现时,我需要注意什么

    建议的方法包括一个预想的隐式转换
    派生*
    → <代码>基*
    用于COM接口指针

    请注意,
    派生时*