【问题标题】:Cast DirectX interfaces to IUnknown pointers将 DirectX 接口转换为 IUnknown 指针
【发布时间】:2014-05-02 23:17:42
【问题描述】:

我的代码中有很多接口,我想将重复的Release 代码封装在另一个方法中,而不是宏中,因为这是 C++,我讨厌使用宏。我最初的尝试是写一个方法,比如

void SafeRelease(IUnknown **ppInterface) {
    if(*ppInterface) {
        (*ppInterface)->Release();
        (*ppInterface) = nullptr;
    }
}

但是将此方法应用于IDirect3DSurface9 *,例如像SafeRelease(&mySurface) 这样会产生错误IDirect3DSurface9 ** is incompatible with IUnknown **。

  1. 我在这里做错了什么?
  2. 是否有更好的方法(希望不使用宏)来实现这样的功能?

【问题讨论】:

  • 有理由创建这样的函数而不是使用ComPtr吗?

标签: c++ memory-management com directx iunknown


【解决方案1】:

这是我的方法:

template <typename T> void SafeRelease(T*& ptr)
{
    if(ptr)
    {
        ptr->Release();
        ptr = nullptr;
    }
}

示例用法:

IDirect3DDevice9 *pD3DDevice = NULL;
d3d->CreateDevice(..., &pD3DDevice);
SafeRelease(pD3DDevice);

如果你愿意,你可以inline这个函数。

【讨论】:

  • +1。带有*&amp; 的模板比我最初的尝试和金属的建议更方便!
【解决方案2】:

您可以使用模板:

template<class DXInterface>
void SafeRelease(DXInterface **ppInterface) {
    if(*ppInterface) {
        (*ppInterface)->Release();
        (*ppInterface) = nullptr;
    }
}

您也可以使用 std::unique_ptr 或 std::shared_ptr 来自动清理:

#include <memory>
#include <iostream>

struct Releaser {
   template<class DXInterface>
   void operator()(DXInterface *pInterface) const {
       if(pInterface) {
           pInterface->Release();
       }
   }
};

// For illustrative purposes only (supplied in DX9 headers)
struct IDirect3DSurface9 { void Release() { std::cout << "Released surface\n";} };
struct IDirect3DTexture9 { void Release() { std::cout << "Released texture\n";} };

void DX9CreateSurface( IDirect3DSurface9** surface ) 
{ 
    *surface = new IDirect3DSurface9();
}

void DX9CreateTexture( IDirect3DTexture9** texture ) 
{ 
    *texture = new IDirect3DTexture9();
}

// Your factory functions
IDirect3DSurface9* createSurface( /*init params go here*/ )
{
    IDirect3DSurface9* surface;
    DX9CreateSurface( &surface );
    return surface;
}

IDirect3DTexture9* createTexture( /*init params go here*/ )
{
    IDirect3DTexture9* texture;
    DX9CreateTexture( &texture );
    return texture;
}

int main()
{
  typedef std::unique_ptr<IDirect3DSurface9, Releaser> SurfacePtr;
  typedef std::unique_ptr<IDirect3DTexture9, Releaser> TexturePtr;

  SurfacePtr surface( createSurface() );
  TexturePtr texture( createTexture() );
  // ... use surface and texture here
  // Automatically released here when their lifetimes ends.
}

请注意,它们使用相同的 Releaser,并注意对 surface.reset() 的调用也会释放接口,并将 unique_ptr 内的指针设置为 null 以启动。这两个对象可能是您的类的成员,而不是 main() 中的对象。

【讨论】:

  • 这样我就必须为我正在使用的所有 Direct3D 接口定义这个方法,这些接口需要发布。这就是我想使用IUnknown的原因。
  • 最好把 Releaser 的 operator() 做成模板。
  • 从技术上讲,编译器会为您定义所有这些,而且不管您是否将其设为模板,它几乎肯定会内联该函数。另请参阅我关于使用智能指针的更新。
  • 我目前没有使用智能指针,因此这是老式的方式。为了创建这样一个接口,我依赖于像CreateOffscreenPlainSurface 这样的函数,它接受一个标记为_Out_ 的IDirect3DSurface9 ** 参数。我怎样才能让函数访问我的智能指针,因为它肯定不会通过传递unique_ptr&lt;IDirect3DSurface9&gt; 来工作,不是吗?
  • 如果它需要一个双指针,那么你需要使用一个单独的对象(或你自己的小工厂函数),然后把它交给智能指针来管理。如果它只使用单个指针,那么您可以调用 unique_ptr::get()。
【解决方案3】:

我在这里做错了什么?

我也有同样的问题,也是针对 COM SafeRelease。所以这里是:

void SafeRelease(IUnknown **ppInterface) 
...
IDirect3DSurface9 * mySurface = new ...
...
SafeRelease(&mySurface);

IDirect3DSurface9 *,凭借继承,可以强制转换为IUnknown *。 但是,与直觉相反,IDirect3DSurface9 ** 不能转换为 IUnknown **。 如果允许,那么在您的 SafeRelease(IUnknown**) 中,您可以执行以下操作:

// obtain a pointer to an instance of some random subinterface of IUnknown
*ppInterface = pMyRamdomComInterfacePointer;

因此,我们会在指向IDirect3DSurface9 的指针中存储指向某个随机IUnknown 导数的指针。这将违反 C++ 类型系统。这就是为什么不允许将除T** 之外的任何其他类型转换为T** 的原因。换句话说,T** 类型的变量只能分配给ppT(T** 类型的值),而不能分配ppSomeSubytpeOfT。

比较这个:How come a pointer to a derived class cannot be passed to a function expecting a reference to a pointer to the base class? 和这个:Casting double pointers of base classes。

对于 COM SafeRelease,可以使用模板(如此处建议的那样)或宏。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-16
    • 2015-01-26
    • 2015-07-21
    • 2020-04-13
    • 2020-01-15
    • 1970-01-01
    • 2013-11-02
    相关资源
    最近更新 更多