カスタム削除子

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のタイミングがいまいち良く分からなくて結局そのままに・・・。