スマートポインタ(参照カウント型)

概要

C/C++の話なんですが、メモリ管理ってめんどくさいですよね。
誰が生成して誰が消去するのか、2度もdeleteすると危ないです。


そんなわけで、自動的に解放してくださるクラスを作りましょう。


制作方針

C/C++のtemplateの機能を使って実現します。
以下の項目が若干不便ですが、ものすごく便利です。
宣言及び定義はヘッダに書かないといけない。
typenameなどがめんだくさい。
その代わりに柔軟性に富んだシステムを設計可能です。


今回は参照カウンタと言うものを定義してコピーのような処理の場合は
オブジェクトを使いまわすような方法で対応しています。


ダウンロード

サンプルソース

#ifndef _SMART_PTR_
#define _SMART_PTR_

#define SAFE_DELETE(p) if(p){delete p;p=0;}

//-----------------------------------------------------
// SmartPtr ... manage delete pointer
//-----------------------------------------------------
template<typename T> 
class SmartPtr
{
public:
	explicit SmartPtr(T* ptr=0);
	SmartPtr(const SmartPtr<T>& ptr);
	virtual ~SmartPtr();

	const SmartPtr<T>& operator=(T* ptr);
	const SmartPtr<T>& operator=(const SmartPtr<T>& ptr);

	const T& operator*();
	T* const operator->();

private:
	T* mpPtr;
	int* mpCnt;

	void release();
};


//-----------------------------------------------------
// Constructor
//-----------------------------------------------------
template<typename T> SmartPtr<T>::SmartPtr(T* ptr)
{
	mpPtr = ptr;
	mpCnt = 0;
	if( ptr ) mpCnt = new int(1);
}

template<typename T> SmartPtr<T>::SmartPtr(const SmartPtr<T>& ptr)
{
        mpPtr = ptr.mpPtr;
        mpCnt = ptr.mpCnt;
	if( mpCnt ) (*mpCnt)++;
}

//-----------------------------------------------------
// Destructor
//-----------------------------------------------------
template<typename T> SmartPtr<T>::~SmartPtr()
{
	release();
}

template<typename T> void SmartPtr<T>::release()
{
	if( mpCnt )
	{
		(*mpCnt)--;
		if( (*mpCnt) > 0 ) return;

		SAFE_DELETE(mpCnt);
		SAFE_DELETE(mpPtr);	
	}
}

//-----------------------------------------------------
// operator input
//-----------------------------------------------------
template<typename T> const SmartPtr<T>& SmartPtr<T>::operator=(T* ptr)
{
	release();

	mpPtr = ptr;
	mpCnt = 0;
	if( ptr ) mpCnt = new int(1);

	return (*this);
} 

template<typename T> const SmartPtr<T>& SmartPtr<T>::operator=(const SmartPtr<T>& ptr)
{
	release();

	mpPtr = ptr.mpPtr;
	mpCnt = ptr.mpCnt;
	if( mpCnt ) (*mpCnt)++;

	return (*this);
}

//-----------------------------------------------------
// operator output
//-----------------------------------------------------
template<typename T> const T& SmartPtr<T>::operator*()
{
	return *mpPtr;
}

template<typename T> T* const SmartPtr<T>::operator->()
{
	return mpPtr;
}

#endif // _SMART_PTR_


mpCntは参照カウンタ
mpPtrは管理する対象のポインタ


SAFE_DELETEについてはメモリを安全に解放するマクロを参照してください。
あと、constはずすとエラーになったりするので注意してくださいね。


小ネタ:publicを先に書くといいらしい。(他人が必要なのは実装ではなく利用方法だとか)

テストコード

#include <iostream>

class A
{
private:
	char a;
public:
	A(char _a):a(_a){ std::cout << a << " CONSTRUCTOR" << std::endl; }
	~A(){ std::cout << a << " DESTRUCTOR" << std::endl; }
	void put(){ std::cout << "THIS IS " << a << std::endl; }
};


SmartPtr<A> getA()
{
	SmartPtr<A> a;
	a = new A('C');

	return a;
}


int main()
{
	std::cout << "START" << std::endl;

	SmartPtr<A> a( new A('A') );
	SmartPtr<A> b( new A('B') );
	a = b;
	a->put();
	a = getA();
	b = a;
	a->put();

	std::cout << "END" << std::endl;
	return 0;
}


すべての参照が消えたときに解放されます。
つまり、mpCntが0になったときに解放されます。

実行してみると

START
A CONSTRUCTOR
B CONSTRUCTOR
A DESTRUCTOR
THIS IS B
C CONSTRUCTOR
B DESTRUCTOR
THIS IS C
END
C DESTRUCTOR

きちんと解放されているみたいです。

使い道

Factoryクラス等で生成したオブジェクトの管理に使えます。
削除を意識する必要がないので便利です。


アロケータを考えるとまた色んな実装があるんだけれども
これくらいならシンプルで使いやすいです。