スマートポインタ

テンプレート嫌いのせいもあって全然触ってなかったboostだけど、
ちょこっとだけshared_ptrを使ってみたらすごく便利で偉く感動してしまったので、どうやって実装してるのかなぁと考えながら実装してみました。


どこかまずいかも知れないけどテストコードは動いてるしまぁいいか。
参照カウンタのメモリを動的に作成するのが嫌な感じだけど、そのあたりははプールクラス作って管理すればいいかもね。


VC.NETで確認。
iostreamは嫌いなので使ってません。printf万歳。


shared_ptrの中身を見たら、!とかもオーバーロードしてたのでやっぱりそこまでやんないとだめっすよ
&NULLチェックもしろよ見たいな感じ。
明示的なDeleteも必要かな。細かいこと言い出すとキリがない。
後、tabが8で表示されるのが気持ち悪い。

#include "stdafx.h"
#include <windows.h>
#include <conio.h>

// オリジナル共有ポインタ.
template<class T> class my_shared_ptr
{
private:
	DWORD	*m_pdwRefCount;	// 参照カウント共有メモリ.
	T		*m_pRef;		// 参照ポインタ.

// 公開.
public:
	my_shared_ptr()
	{
		Zero();
	}

	my_shared_ptr(T *p)
	{
		Zero();
		if ( p )
		{
			m_pRef = p;
			m_pdwRefCount = new DWORD;
			*m_pdwRefCount = 0;
			IncRef();
		}
	}

	// コピーコンストラクタ.
	my_shared_ptr( const my_shared_ptr<T> &p )
	{
		Zero();
		*this = p;
	}

	~my_shared_ptr()
	{
		DecRef();
	}

	// 参照演算子オーバーロード.
	T *operator ->()
	{
		return m_pRef;
	}

	void operator = ( const my_shared_ptr<T> &ptr )
	{
		// すでに何かしかの参照を得ている場合参照カウンタを減らし.
		// その後でコピーを実行する.
		DecRef();

		// コピー.
		m_pdwRefCount = ptr.m_pdwRefCount;
		m_pRef = ptr.m_pRef;
		
		// 参照回数を増やす.
		IncRef();
	}

	// 参照を変更する.
	void Reset( T *p )
	{
		DecRef();

		// コピー.
		m_pRef = p;

		// 参照カウンタの作成.
		// 仮にDecRefでpdwRefCountが0になっていない場合はほかで参照があるのでたぶん大丈夫.
		m_pdwRefCount = new DWORD;
		*m_pdwRefCount = 0;

		IncRef();
	}
	
	inline DWORD GetCount(void)
	{
		if ( !m_pdwRefCount ) return 0;
		return (*m_pdwRefCount);
	}

// 非公開関数.
private:
	void IncRef(void)
	{
		if ( !m_pdwRefCount ) return;
		(*m_pdwRefCount)++;
	}

	void DecRef(void)
	{
		if ( !m_pdwRefCount ) return;

		(*m_pdwRefCount)--;
		// 参照が無くなったら開放!.
		if ( *m_pdwRefCount == 0 )
		{
			// 開放.
			delete m_pRef;
			delete m_pdwRefCount;
			m_pdwRefCount = NULL;
			m_pRef = NULL;
		}
	}
	
	void Zero(void)
	{
		m_pdwRefCount = NULL;
		m_pRef = NULL;
	}
};

class A
{
public:
	int a;
	A()
	{
		a = 0;
		printf("construct a = %d\n", a);
	}
	A(int val)
	{
		a = val;
		printf("construct a = %d\n", a);
	}
	~A()
	{
		printf("destruct a = %d\n", a);
		a = 0;
	}

};

// class Aを引数に取る関数はどうなるか。
// コピーコンストラクタが呼ばれる.
void Test( my_shared_ptr<A> ptr )
{
	printf("ref = %d\n", ptr.GetCount() );
}

// 参照はどうか..
void TestRef( my_shared_ptr<A> &ptr )
{
	printf("ref = %d\n", ptr.GetCount() );
}


int _tmain(int argc, _TCHAR* argv[])
{
	{
		my_shared_ptr<A> ptrA1;
		{
			my_shared_ptr<A> ptrA( new A );

			ptrA1 = ptrA;	// 参照が増える.	

			Test( ptrA );	// 値渡しの場合も参照が増える.

			TestRef( ptrA );	// 参照渡しの場合は増えないし、減らない

			getch();

			// セットしなおす.
			ptrA.Reset( new A(100) );	

			getch();
		}
	}
	getch();
	return 0;
}