Имея в своем коде немало интерфейсов, я хотел инкапсулировать повторяющиеся Release
код в другом методе, а не в макросе, потому что это C ++, и я ненавижу использовать макросы. Моей первоначальной попыткой было написать такой метод, как
void SafeRelease(IUnknown **ppInterface) {
if(*ppInterface) {
(*ppInterface)->Release();
(*ppInterface) = nullptr;
}
}
Однако применение этого метода к IDirect3DSurface9 *
например лайк SafeRelease(&mySurface)
выдает ошибку IDirect3DSurface9 **
несовместимо с IUnknown **
,
Вот мой метод:
template <typename T> void SafeRelease(T*& ptr)
{
if(ptr)
{
ptr->Release();
ptr = nullptr;
}
}
Пример использования:
IDirect3DDevice9 *pD3DDevice = NULL;
d3d->CreateDevice(..., &pD3DDevice);
SafeRelease(pD3DDevice);
Ты можешь inline
эта функция, если хотите.
Вы можете использовать шаблон:
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 ().
Что я здесь делаю не так?
У меня просто был такой же вопрос, тоже для 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;
И таким образом мы бы сохранили указатель на некоторый случайный IUnknown
производная в указателе на IDirect3DSurface9
, Это нарушило бы систему типов C ++. Вот почему приведение любого другого типа, кроме T**
в T**
не допускается. Другими словами, переменная типа T**
может быть назначен только ppT
(значение типа T**
) а не ppSomeSubytpeOfT
,
Сравните это: Почему указатель на производный класс не может быть передан функции, ожидающей ссылку на указатель на базовый класс? И этот: Кастинг двойных указателей базовых классов.
Для COM SafeRelease
подойдет либо шаблон (как предложено здесь), либо макрос.