BREW Array(3)
普通に BrewRefObject クラスを継承して length をメンバに持っても、スライシングが発生してしまって、キャストがうまくいかない。
どうすればいいかというと、配列へのポインタとその長さを持ったクラス(BrewArrayPtr)へのポインタを、BrewRefObject クラスのメンバ、_obj に割り当て、Deleter を登録するときに、BrewArrayPtr へのポインタと、BrewArrayPtr が持っている配列へのポインタの両方を解放する Deleter を登録するようにすればいいのだ。
具体的にどうするかというと、まずは配列へのポインタと、その長さを持ったクラスを用意する。
// 配列へのポインタと、その長さを持ったクラス // pool_memory を継承すると、そのクラスはメモリプールから確保される class BrewArrayPtr : public pool_memory{ public: BrewArrayPtr( void* p , int len ) : pObj( p ) , length( len ){} void* pObj; int length; };
これは配列へのポインタと、その長さを持っただけという、ただそれだけのクラスだ。
そして、このクラスを解放するための Deleter を用意するわけだけれども、このとき、BrewArrayPtr が持っている配列へのポインタも同時に解放する。
// BrewArrayPtr と、内部に持っている pObj を解放するための Deleter。 // pObj はメモリプールから確保されている template< class T > class BrewArrayPtrPoolDeleter : public BrewRefCountDeleterBase{ public: virtual void Delete( void* p ){ BrewArrayPtr* ptr = (BrewArrayPtr*)p; // pObj のデストラクタを呼び出す for( int i = ptr->length - 1 ; i >= 0 ; i-- ){ (&(((T*)ptr->pObj)[ i ]))->~T(); } // pObj を解放 pool_memory::operator delete( ptr->pObj ); // BrewArrayPtr を解放 delete ptr; } };
あとは、配列クラスを用意するだけだ。
// 配列クラス template< class T > class BrewArray : public BrewRefObject{ public: // デフォルトコンストラクタ BrewArray(){} // コピーコンストラクタ BrewArray( const BrewRefObject& s ) : BrewRefObject( s ){} // null に対するコンストラクタ BrewArray( long ) : BrewRefObject( null ){} // 代入演算子 BrewArray& operator=( const BrewRefObject& s ){ return *(BrewArray*)&BrewRefObject::operator=( s ); } // null に対する代入演算子 BrewArray& operator=( long ){ return *(BrewArray*)&BrewRefObject::operator=( null ); } void Set( int num ){ // InnerRelease ではダメ Release(); if( num <= 0 ) return; // メモリプールから必要なメモリを確保 T* ptr = (T*)pool_memory::operator new( sizeof( T ) * num ); // コンストラクタ呼び出し for( int i = 0 ; i < num ; i++ ){ new( &ptr[ i ] ) T(); } // BrewArrayPtr と BrewArrayPtrPoolDeleter はメモリプールから確保される _pObj = new BrewArrayPtr( ptr , num ); _pRef = new BrewArrayPtrPoolDeleter< T >(); } // 演算子オーバーロード __inline T& operator[]( int index ) const{ return get()[ index ]; } __inline T* operator->() const{ return get(); } __inline T& operator*() const{ return *get(); } // オブジェクトへのポインタを取得 // _pObj の null チェックは必須 __inline T* get() const{ if( _pObj == null ) return null; return (T*)((BrewArrayPtr*)_pObj)->pObj; } // オブジェクトの長さを取得 // _pObj の null チェックは必須ではない __inline int length() const{ if( _pObj == null ) return 0; return ((BrewArrayPtr*)_pObj)->length; } };
注意すべき点は、get() で必ず null チェックをすること。
もしこの null チェックをしなかった場合、
BrewRefObject obj; BrewArray< Hoge > hoge; obj.get(); hoge.get();
この4行目でアクセス違反を起こすことになってしまう。上位クラスの BrewRefObject は null を返すのに、だ。
なので、この null チェックは必須だろう。ただし、length() はどちらでも構わないと思う。
null なオブジェクトに対して length を取るとアクセス違反を起こすのは自然だろうから。
これで、2の問題は解決する。
このクラスに対して1の方法を足せば、両方満たしたクラスの完成だ。
昨日は2の問題は難しいとか言ったけど、別にそんなに難しい問題でもなかったかも(´・ω・`)