たまにはプログラマらしく2 〜さよならデストラクタ〜

たまにはプログラマらしく2 〜さよならデストラクタ〜

どうも、tktkです。

今回はC++0x(C++11)のunique_ptrのことを少々。
ですので、プログラムに関心が無い方はここで読むのをやめちゃって構いません。

unique_ptrはスマートポインタ(わからない人はググってね)の一つです。

//////////////////////////////////////////////////////
int main(){
 std::unique_ptr<int> p(new int()); // 普通の使い方
 std::unique_ptr<int[]> pa(new int[10]);// 配列もOK
}// 関数を抜ける時にdelete, delete[]が呼ばれる
//////////////////////////////////////////////////////

こいつの素晴らしいところはデリータを指定できるというところです。
つまりこちら側で開放時の処理を記述できるので、DirectX等で使用するポインタに使えます。

///////////////////////////////////////////////////////
struct Releaser{
void operator(IDirect3D9* ptr){ ptr->Release(); }
};

std::unique_ptr<IDirect3D9, Releaser> _g_d3d;

int DirectXInit(HWND hWnd){
 _g_d3d.reset( Direct3DCreate9( D3D_SDK_VERSION ) ); // 設定するときはreset
 if(!_g_d3d)return 0; // operator boolでnullptrチェック
 return 1;
}
////////////////////////////////////////////////////////

上記のプログラムは_g_d3dが破棄されるときにRelease()関数が呼ばれます。

ありがたいことにDirectX関係のクラスはだいたい(全部調べたわけではないのですいません)
Release()で開放処理をするので、Releaserのoperator()をテンプレートにします。

////////////////////////////////////////////////////////
struct Releaser{
 template<class T>
 void operator(T* ptr){ ptr->Releaser(); }
};
////////////////////////////////////////////////////////

この上記のReleaserを利用すると

/////////////////////////////////////////////////////////
std::unique_ptr<IDirect3D9, Releaser> _g_d3d;
std::unique_ptr<IDirect3DDevice9, Releaser> _g_device;

int DirectXInit(HWND hWnd){
 _g_d3d.reset( Direct3DCreate9( D3D_SDK_VERSION ) ); // 設定するときはreset
 if(!_g_d3d)return 0; // operator boolでnullptrチェック
 IDirect3DDevice9* device = nullptr;
 if(FAILED(_g_d3d->CreateDevice(/*引数は省略*/, &device)))return 0;
 _g_device.reset(device);
 if(!_g_device)return 0;
 return 1;
}
////////////////////////////////////////////////////////

上記のプログラムは_g_d3d、_g_deviceが破棄されるときにRelease()関数が呼ばれます。
他にもIDirect3DTexture9やIDirect3DSurface9などでも同じように使えます。

これまではDirectX関係のポインタを使用する場合、どこかでRelease()を呼ぶ処理が必要でしたが、
こいつを利用すればその部分を省くことができます。

//////////////////////////////////////////
class DXManagerOld{// これまでのDirectX管理クラス
 IDirect3D9* _d3d;
 IDirect3DDevice9* _device;
public:
 DXManagerOld() : _d3d(nullptr), _device(nullptr){}
 ~DXManagerOld(){ Release(); }// デストラクタでRelease()を呼ぶ
 bool Init(HWND hWnd);
 void Release(){// 開放関数
  if(nullptr != _d3d)_d3d->Release();
  if(nullptr != _device)_device->Release();
 }
 IDirect3DDevice9* GetDevice(){ return _device; }
};

class DXManagerNew{// unique_ptrで管理する場合
 std::unique_ptr<IDirect3D9, Releaser> _d3d;
 std::unique_ptr<IDirect3DDevice9, Releaser> _device;
public:
 DXManagerOld() : _d3d(), _device(){}
 // デストラクタがいらない!
 bool Init(HWND hWnd);
 // Release()がいらない!
 IDirect3DDevice9* GetDevice(){ return _device.get(); }// 生のアドレスが欲しい場合はget()
};
//////////////////////////////////////////

上記のDXManagerOldように「仕方なくデストラクタを作っていた」というものは多いと思います。
しかし、unique_ptrとデリータを上手く使えばそういったことも無くなります。

こいつはグレートだぜ・・・

他にも色々あるのですが、長くなるのでやめます(中途半端ですいません)
気になった方はunique_ptrを調べてみてください。

unique_ptrを上手く使ってデストラクタとさよならしましょう。




tktk