カスタム削除子
C++テンプレートテクニック 7-3
スマートポインタのメモリ解放処理を関数ポインタ(もしくは関数オブジェクト)を渡すことによって専用の解放処理を実行するという話。
以前DirectX用のスマートポインタを作ったことがあるので、今回はそれにカスタム削除子を追加してみることにします。
// 非テンプレート基本クラス class com_ptr_deleter_base { public: virtual ~com_ptr_deleter_base() {}; virtual void destroy () = 0; }; // テンプレート派生クラス template<class T,class D> class com_ptr_deleter : public com_ptr_deleter_base { private: T* m_pComObj; D m_deleter; public: typedef com_ptr_deleter<T,D> type; com_ptr_deleter(T* comObj,D deleter) { m_pComObj = comObj; m_deleter = deleter; } virtual void destroy () { m_deleter(m_pComObj); } virtual ~com_ptr_deleter() { } }; template<class T> class com_ptr { private: T* m_pComObj; com_ptr_deleter_base* m_pDeleter; public: explicit com_ptr (T* comObj = NULL) { m_pComObj = comObj; m_pDeleter = NULL; } // カスタム削除子を受け取るメンバ関数テンプレート template<class D> explicit com_ptr (T* comObj,D deleter) { m_pComObj = comObj; m_pDeleter = new com_ptr_deleter<T,D>(m_pComObj,deleter); } // コピーコンストラクタ com_ptr(const com_ptr<T>& comObj) { // コピーコンストラクタの場合はAddRef呼ぶだけ m_pComObj = comObj.m_pComObj; m_pDeleter = comObj.m_pDeleter; if ( m_pComObj ) { m_pComObj->AddRef(); } } // 代入処理 com_ptr<T>& operator=(const com_ptr<T>& comObj) { // 現在所有しているポインタはReleaseで解放 if ( m_pComObj ) { if ( m_pDeleter ) { m_pDeleter->destroy(); } else { m_pComObj->Release(); } } // ポインタのコピー m_pComObj = comObj.m_pComObj; m_pDeleter = comObj.m_pDeleter; // コピーが発生したのでAddRefする if ( m_pComObj ) { m_pComObj->AddRef(); } return *this; } // DirectXの生成系関数にポインタを渡すときに使用する T** creator_ptr () { // 生成時にAddRefされるので予めReleaseで参照カウンタを減らしておく if ( m_pComObj ) { m_pComObj->Release(); } return &m_pComObj; } // DirectXの関数にポインタを渡すときに使用する T* ptr () { return m_pComObj; } // 保持しているポインタを透過的に扱えるようにする T* operator->() { return m_pComObj; } // ポインタを保持しているかどうかをチェックする operator bool () { return (m_pComObj != NULL); } ~com_ptr () { if ( m_pComObj ) { if ( m_pDeleter ) { m_pDeleter->destroy(); } else { m_pComObj->Release(); } } } };
#include <iostream> #include "smart_ptr.h" using std::cout; using std::endl; class CSample { private: int m_refCount; public: CSample () { m_refCount = 1; cout << this << " Create : " << m_refCount << endl; } void AddRef () { m_refCount++; cout << this << " AddRef : " << m_refCount << endl; } void Release() { m_refCount--; cout << this << " Release : " << m_refCount << endl; } void output () { cout << "output" << endl; } }; void func (com_ptr<CSample>) { } void deleter (CSample* p) { cout << "カスタム削除子" << endl; p->Release(); } int main () { com_ptr<CSample> obj1(new CSample,deleter); com_ptr<CSample> obj2(new CSample,deleter); obj1->output(); obj1 = obj2; obj1->output(); return 0; }
$ main 00384F48 Create : 1 00382FE0 Create : 1 output カスタム削除子 00384F48 Release : 0 00382FE0 AddRef : 2 output カスタム削除子 00382FE0 Release : 1 カスタム削除子 00382FE0 Release : 0
・・・。やってみたものの、色々難しい。m_pDeleterのdeleteのタイミングがいまいち良く分からなくて結局そのままに・・・。