DirectX用のスマートポインタの作成

DirectX関連のオブジェクトを使用する場合に便利なスマートポインタを自作しました。

// -- smart_ptr.h --
#include <memory>

namespace ddp {
    template<class T>
    class com_ptr {
    private:
        T* m_pComObj;
    
    public:
        explicit com_ptr (T* comObj = NULL) {
            m_pComObj = comObj;
        }
        
        // コピーコンストラクタ
        com_ptr(const com_ptr<T>& comObj) {
            // コピーコンストラクタの場合はAddRef呼ぶだけ
            m_pComObj = comObj.m_pComObj;
            if ( m_pComObj ) {
                m_pComObj->AddRef();
            }
        }
        
        // 代入処理
        com_ptr<T>& operator=(const com_ptr<T>& comObj) {
            // 現在所有しているポインタはReleaseで解放
            if ( m_pComObj ) {
                m_pComObj->Release();
            }
            
            // ポインタのコピー
            m_pComObj = comObj.m_pComObj;
            
            // コピーが発生したのでAddRefする
            if ( m_pComObj ) {
                m_pComObj->AddRef();
            }
            
            return *this;
        }
        
        // DirectXの生成系関数にポインタを渡すときに使用する
        T** creator_ptr () {
            // 生成時にAddRefされるので予めReleaseで参照カウンタを減らしておく
            if ( m_pComObj ) {
                m_pComObj->Release();
            }
            return &m_pComObj;
        }
        
        // 保持しているポインタを透過的に扱えるようにする
        T* operator->() {
            return m_pComObj;
        }
        
        // ポインタを保持しているかどうかをチェックする
        operator bool () {
            return (m_pComObj != NULL);
        }
        
        ~com_ptr () {
            if ( m_pComObj ) {
                m_pComObj->Release();
            }
        }
    };
    
    // shared_ptrを取り込む
    using std::tr1::shared_ptr;
};

ddp::com_ptrという名前です。このddpという名前空間はとりあえず自分用ライブラリということで。

また、普通のオブジェクト用にshared_ptrを自作しようと思ったんですが、これはわざわざ作らなくてもTR1に既に存在しているのでusingでddpに取り込むだけにしました。

以下動作確認の例です。

#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 (ddp::com_ptr<CSample>) {
}

int main () {
    ddp::com_ptr<CSample> obj1(new CSample);
    ddp::com_ptr<CSample> obj2 = obj1;
    ddp::com_ptr<CSample> obj3;
    obj3 = obj1;
    func(obj1);
    func(obj2);
    func(obj3);
    obj3 = ddp::com_ptr<CSample>(new CSample);
    func(ddp::com_ptr<CSample>(new CSample));
    
    obj3->output();
    return 0;
}
$ main
00384FC8 Create  : 1
00384FC8 AddRef  : 2
00384FC8 AddRef  : 3
00384FC8 AddRef  : 4
00384FC8 Release : 3
00384FC8 AddRef  : 4
00384FC8 Release : 3
00384FC8 AddRef  : 4
00384FC8 Release : 3
00382E90 Create  : 1
00384FC8 Release : 2
00382E90 AddRef  : 2
00382E90 Release : 1
00382EA0 Create  : 1
00382EA0 Release : 0
output
00382E90 Release : 0
00384FC8 Release : 1
00384FC8 Release : 0

うまくいってるっぽいです。ちゃんと最後には参照カウンタが0になっています。