bate's blog

調べたこと実装したことなどを取りとめもなく書きます。

参照カウンタ付きのキャッシュ

大雑把に実装してみた。
もっと抽象化できると思う。
まずは、main.cpp

// main.cpp
#include<iostream>

#include "CacheManager.h"

using namespace std;

// シングルトンお試し用
void init();
void run();
void end();


void disp(int num, InterfaceResource **ress)
{
	for(int i=0; i<num; ++i)
	{
		if(ress[i])
			cout << "+ " << i << " : " << ress[i]->getName() << ", " << ress[i] << endl;
		else
			cout << "+ " << i << " : " << ress[i] << endl;
	}
}

int main()
{
	#define cm CacheManager::GetInstance()
	
	init();
	run();
	end();
	
	return 0;
}

void init()
{
	cm->init();
}

void run()
{
	InterfaceResource* res[8];
	cout << "-------------- init" << endl;
	cm->dispCache();
	
	// リソース取得
	string name[5] = { "aaa", "bbb", "ccc", "ddd", "eee" };
	for(int i=0; i<8; ++i)
		res[i] = cm->getRes(name[i%4]);
	
	cout << "-------------- after get Resources" << endl;
	cm->dispCache();
	disp(8, res);
	
	// res[2]とres[6]を開放
	cm->releaseRes(res[2]);
	cm->releaseRes(res[6]);
	
	cout << "-------------- after release index 2 and 6" << endl;
	cm->dispCache();
	disp(8, res);
	
	// res[2]に別リソース取得
	res[2] = cm->getRes(name[4]);
	
	cout << "-------------- after res[2] get other Resource" << endl;
	cm->dispCache();
	disp(8, res);
	
	// res[0]からres[3]まで開放
	for(int i=0; i<4; ++i)
		cm->releaseRes(res[i]);

	cout << "-------------- after release some Resources" << endl;
	cm->dispCache();
	disp(8, res);
	
	// 全部開放
	for(int i=0; i<8; ++i)
		cm->releaseRes(res[i]);
	
	cout << "-------------- after release all Resources" << endl;
	cm->dispCache();
	disp(8, res);
}

void end()
{
	cm->init();
}

キャッシュの内容と、リソース取得用配列の内容を表示。
+ が付いているのが後者。

リソースのインターフェイス
こいつで操作。
本題ではないので簡素。

// InterfaceResource.h
#ifndef __InterfaceResource__
#define __InterfaceResource__

#include <string>

using namespace std;

class InterfaceResource
{
public:
	virtual ~InterfaceResource() {}
	
	virtual void showName() = 0;
	virtual string getName() = 0;
};

#endif	// __InterfaceResource__
// Resource.h
#ifndef __Resource__
#define __Resource__

#include "InterfaceResource.h"

#include <iostream>
#include <string>

using namespace std;

class Resource : public InterfaceResource
{
public:
	Resource(string name);
	virtual ~Resource() { }
	void showName();
	string getName();
	
private:
	string m_name;
};

#endif	// __Resource__
// Resource.cpp

#include "Resource.h"

Resource::Resource(string name)
: m_name(name)
{
}

void Resource::showName()
{
	cout << m_name << endl;
}

string Resource::getName()
{
	return m_name;
}

キャッシュ経由でリソースの取得と開放を行う。

// CacheManager.h
#ifndef __CacheManager__
#define __CacheManager__

#include "InterfaceResource.h"

// キャッシュと参照カウンタ
struct Cache
{
	int RefCount;
	string Name;		// IDの代わり(Resourceを識別するものを使う)
	InterfaceResource* resource;
};

// キャッシュと参照カウンタを管理
class CacheManager
{
	enum{ CACHE_MAX=8 };
	
public:
	InterfaceResource* getRes(string name);
	void releaseRes(InterfaceResource* (&res));	// ポインタを参照渡し
	
	void dispCache();
	void init();
	
private:
	Cache m_cache[CACHE_MAX];

	// シングルトン
public:
	// 唯一のアクセス
	static CacheManager* GetInstance()
	{
		static CacheManager instance;	// 唯一のインスタンス
		return &instance;
	}
	
	virtual ~CacheManager() { }
	
private:
	// 生成やコピーを禁止
	CacheManager() { }
	CacheManager(const CacheManager& rhs);
	CacheManager& operator=(const CacheManager& rhs);
};

#endif	// __CacheManager__
// CacheManager.cpp

#include "CacheManager.h"
#include "Resource.h"

static const string NONE = "None";

InterfaceResource* CacheManager::getRes(string name)
{
	int empty_index = -1;
	bool passed = false;
	for(int i = 0; i < CACHE_MAX; ++i)
	{
		if(m_cache[i].Name == name)
		{
			// キャッシュにある
			++m_cache[i].RefCount;
			return m_cache[i].resource;
		}
		else if(!passed && m_cache[i].Name == NONE)
		{
			passed = true;
			empty_index = i;
		}
	}
	// キャッシュに無い
	if(passed)
	{
		// 空いている場所に作成
		++m_cache[empty_index].RefCount;
		m_cache[empty_index].Name = name;
		m_cache[empty_index].resource = new Resource(name);
		return m_cache[empty_index].resource;
	}
	// キャッシュが一杯
	return new Resource(name);
}

void CacheManager::releaseRes(InterfaceResource* (&res))
{
	if(!res) return;
	
	for(int i = 0; i < CACHE_MAX; ++i)
	{
		if(m_cache[i].Name == res->getName())
		{
			--m_cache[i].RefCount;
			res = 0;	// ここが厄介なのでポインタを参照渡し
			if(m_cache[i].RefCount < 1)
			{
				delete m_cache[i].resource;
				m_cache[i].resource = 0;
				m_cache[i].RefCount = 0;
				m_cache[i].Name = NONE;
			}
			return;
		}
	}
	// キャッシュに無い
	delete res;
	res = 0;
}

void CacheManager::dispCache()
{
	for(int i = 0; i < CACHE_MAX; ++i)
	{
		cout << i << " : " << m_cache[i].RefCount << ", "
				<< m_cache[i].Name << ", "
				<< m_cache[i].resource << endl;
	}
}

void CacheManager::init()
{
	for(int i = 0; i < CACHE_MAX; ++i)
	{
		m_cache[i].RefCount = 0;
		m_cache[i].Name = "None";
		m_cache[i].resource = 0;
	}
}