スマートポインタ・2次元配列版

d:id:n-trino:20060828
以前、一般的な多次元配列のを作ったけどいろいろ欠点があったので全然使っていない。ていうかアレは殆どただ作ることを楽しんでただけで実用性とかは考えていなかったし。というわけでもっとな多次元配列を作ろうと考えていたんだけど・・・厳しいのでとりあえずよく使う低い次元だけでもと考えておりました。
というわけで2次元配列版を以前から作ろう作ろうと考えていたけどいい実装方法・インターフェースが思いつかずにほったらかしになっていた。自分自身に要求分析したらえらい大変だったり。

  • 当たり前だが、で呼び出し可能、参照カウンタによる自動開放
  • メモリ確保は1行で・・・じゃなかったら実用性低すぎ(^^;
  • デバッグモードで配列超過に関して例外を投げる
  • 連続メモリを保障
  • []1個だけによる中間生成物を排出、擬似1次元操作可能

というもの。結構悩みました(^^; 最後の奴が分かりにくいかと思うが、要するに2次元のうち1次元を固定した場合は呼び出しのパフォーマンスを下げるために一時データを作ることを可能にしたいということ。手動最適化でよくやる技として

for( int y = 0; y < 10; ++y ){
	for( int x = 0; x < 10; ++x ){
		aa[y][x];
	}
}

ではなく

for( int y = 0; y < 10; ++y ){
	int* a = aa[y];
	for( int x = 0; x < 10; ++x ){
		a[x];
	}
}

とやる方法があるのだが、これをスマートポインタにしても可能にしたいというところ。んでこれ単体なら簡単なのだがこれプラス配列超過チェック付きとなると結構苦しかった(^^;

んで今日無駄に気合いれてみたら案外あっさり出来ちゃいました。

template< class P >
class smart_2D_array
{
public:
	
#ifdef _DEBUG
	//サブ配列。普通の配列の非常に軽いラッピング
	//デバッグモードで配列超過を検出するだけのクラス(ぉ
	class sub_array{
		sub_array( P* pObject, unsigned int nSize ){
			m_pObject = pObject;
			m_nSize = nSize;
		}
	public:
		sub_array( const sub_array& a ){
			m_pObject = a.m_pObject;
			m_nSize = a.m_nSize;
		}
		sub_array& operator=( const sub_array& a ){
			m_pObject = a.m_pObject;
			m_nSize = a.m_nSize;
			return *this;
		}
		P& operator( unsigned int i ){
			if( i < m_nSize ){
				return m_pObject[i];
			}else{
				throw "Buffer Overflow";;
			}
		}
		friend class smart_2D_array;
	private:
		P* m_pObject;
		unsigned int m_nSize;
	};
#else
	typedef P* sub_array;
#endif

	//デフォルトコンストラクタ。
	//・・・・からっぽである。
	smart_2D_array():m_pnCounter( NULL ),m_aaObject( NULL ),m_nSize1( 0 ),m_nSize2( 0 ){}

	//コピーコンストラクタ。
	//アップキャストもOK
	template< class D >
	smart_2D_array( const smart_2D_array< D >& p ){
		if( p != NULL ){
			//ポインタコピー
			m_aaObject = p.m_aaObject;
			m_pnCounter = p.m_pnCounter;
			//参照カウンタ++
			(*m_pnCounter)++;
			//サイズ
			m_nSize1 = p.m_nSize1;
			m_nSize2 = p.m_nSize2;
		}else{
			m_aaObject = NULL;
			m_pnCounter = NULL;
			m_nSize1 = 0;
			m_nSize2 = 0;
		}
	}
	smart_2D_array( const smart_2D_array& p ){
		if( p != NULL ){
			m_aaObject = p.m_aaObject;
			m_pnCounter = p.m_pnCounter;
			(*m_pnCounter)++;
			m_nSize1 = p.m_nSize1;
			m_nSize2 = p.m_nSize2;
		}else{
			m_aaObject = NULL;
			m_pnCounter = NULL;
			m_nSize1 = 0;
			m_nSize2 = 0;
		}
	}

	//生ポインタコンストラクタ(何)
	//サイズで指定
	template< class D >
	smart_2D_array( unsigned int nSize1, unsigned int nSize2 ){
		//制御パラ確保
		m_pnCounter = new unsigned int;
		*m_pnCounter = 1;
		//メモリ確保
		m_aaObject = new P*[nSize1];
		D* aObject = new D[nSize1 * nSize2];
		for( unsigned int i = 0; i < nSize1; ++i ){
			m_aaObject[i] = aObject + i * nSize2;
		}
		//サイズ
		m_nSize1 = nSize1;
		m_nSize2 = nSize2;
	}
	smart_2D_array( unsigned int nSize1, unsigned int nSize2 ){
		//制御パラ確保
		m_pnCounter = new unsigned int;
		*m_pnCounter = 1;
		//メモリ確保
		m_aaObject = new P*[nSize1];
		P* aObject = new P[nSize1 * nSize2];
		for( unsigned int i = 0; i < nSize1; ++i ){
			m_aaObject[i] = aObject + i * nSize2;
		}
		//サイズ
		m_nSize1 = nSize1;
		m_nSize2 = nSize2;
	}


	//代入演算子
	template< class D >
	smart_2D_array< P >& operator =( const smart_2D_array< D >& p ){
		//中身をやさしく削除
		Release();
		if( p != NULL ){
			//メンバコピー
			m_pnCounter = p.m_pnCounter;
			m_aaObject = p.m_aaObject;
			m_nSize1 = p.m_nSize1;
			m_nSize2 = p.m_nSize2;
			//参照カウンタUP!!
			(*m_pnCounter)++;
		}
		return *this;
	}
	smart_2D_array& operator =( const smart_2D_array& p ){
		Release();
		if( p != NULL ){
			m_pnCounter = p.m_pnCounter;
			m_aaObject = p.m_aaObject;
			m_nSize1 = p.m_nSize1;
			m_nSize2 = p.m_nSize2;
			(*m_pnCounter)++;
		}
		return *this;
	}

	//再利用
	template< class D >
	smart_2D_array< P >& Reset( unsigned int nSize1, unsigned int nSize2 ){
		Release();
		//制御パラ確保
		m_pnCounter = new unsigned int;
		*m_pnCounter = 1;
		//メモリ確保
		m_aaObject = new P*[nSize1];
		D* aObject = new D[nSize1 * nSize2];
		for( unsigned int i = 0; i < nSize1; ++i ){
			m_aaObject[i] = aObject + i * nSize2;
		}
		//サイズ
		m_nSize1 = nSize1;
		m_nSize2 = nSize2;
		return *this;
	}
	smart_2D_array< P >& Reset( unsigned int nSize1, unsigned int nSize2 ){
		Release();
		//制御パラ確保
		m_pnCounter = new unsigned int;
		*m_pnCounter = 1;
		//メモリ確保
		m_aaObject = new P*[nSize1];
		P* aObject = new P[nSize1 * nSize2];
		for( unsigned int i = 0; i < nSize1; ++i ){
			m_aaObject[i] = aObject + i * nSize2;
		}
		//サイズ
		m_nSize1 = nSize1;
		m_nSize2 = nSize2;
		return *this;
	}


	//ディストラクタ。
	virtual ~smart_2D_array(){
		Release();
	}

	//明示的開放
	void Release(){
		//ぬるぽ中
		if( m_aaObject == NULL ){
			return;
		}
		//参照カウンタが0なら制御DATAも削除
		if( --(*m_pnCounter) == 0 ){
			delete m_pnCounter;
			delete *m_aaObject;
			delete m_aaObject;
		}
		//初物のふりをする。
		m_pnCounter = NULL;
		m_aaObject = NULL;
		m_nSize1 = 0;
		m_nSize2 = 0;
		return;
	}

	//配列のふりをする。
#ifndef _DEBUG
	sub_array operator( unsigned int i ) const { return m_aaObject[i]; }
#else
	//デバッグ用。配列を超えると例外を投げてくる。
	sub_array operator[]( unsigned int i ) const {
		if( i < m_nSize1 ){
			return sub_array( m_aaObject[i], m_nSize2 );
		}else{
			throw "Buffer Overflow";
		}
	}
#endif

	//比較演算子
	bool operator==( const P** pp ) const{
		return m_aaObject == p;
	}
	bool operator==( const smart_2D_array< P >& pp ) const{
		return m_aaObject == pp.m_aaObject;
	}
	bool operator!=( const P** pp ) const{
		return m_aaObject != pp;
	}
	bool operator!=( const smart_2D_array< P >& pp ) const{
		return m_aaObject != pp.m_aaObject;
	}

	//コンテナ的なこと
#ifdef _DEBUG
	unsigned int GetSize1(){
		if( m_nSize1 == 0xFFFFFFFF ){
			throw "undefined size";
		}else{
			return m_nSize1;
		}
	}
	unsigned int GetSize2(){
		if( m_nSize2 == 0xFFFFFFFF ){
			throw "undefined size";
		}else{
			return m_nSize2;
		}
	}
	unsigned int GetSize(){
		return GetSize1() * GetSize2();
	}
#else
	unsigned int GetSize1(){ return m_nSize1; }
	unsigned int GetSize2(){ return m_nSize2; }
	unsigned int GetSize(){ return m_nSize1 * m_nSize2; }
#endif
	P** Get2DPtr(){ return m_aaObject; }
	P* GetPtr(){ return *m_aaObject; }


	template< class Y > friend class smart_2D_array;

private:
	unsigned int* m_pnCounter;
	P** m_aaObject;

	unsigned int m_nSize1;
	unsigned int m_nSize2;

};

さて、使い方は殆ど普通の配列と同じですが最後の要求に関しては注意がちょい必要で、

for( int y = 0; y < 10; ++y ){
	smart_2D_array< int >::sub_array a = aa[y];
	for( int x = 0; x < 10; ++x ){
		a[x];
	}
}

という形になります。なんだか不恰好ですが一応要求は満たしています(^^; これちゃんと配列超過チェックも行なっているしリリース時には生ポインタになってくれて速度も出てくれます。

さて、実用性の程は・・・不明です(ぇ