BREW Array

BREW で配列を扱う方法について考えてみる。


配列クラスの実装は、メモリプールからメモリを確保するのであればものすごく簡単で、単純に実装出来る。

/*!
 * @brief 配列を表すためののクラス
 * 
 * 配列を生成する方法を除けば、Javaの配列と同じ記法で書くことが出来る
 */
template< class T >
class Array{
public:
    /*!
     * @brief デフォルトコンストラクタ
     */
    Array() : length( 0 ) , _pObj( null ) , _pRef( null ){}
    /*!
     * @brief デストラクタ
     */
    ~Array(){
        release();
    }
    
    /*!
     * @brief コンストラクタ
     * 
     * @param num 生成する配列の数
     */
    explicit Array( int num ) : length( 0 ) , _pObj( null ) , _pRef( null ){
        set( num );
    }
    
    /*!
     * @brief コピーコンストラクタ
     */
    Array( const Array& s ) : length( 0 ) , _pObj( null ) , _pRef( null ){
        set( s );
    }
    
    /*!
     * @brief 代入演算子
     */
    Array& operator=( const Array& s ){
        set( s );
        return (*this);
    }
    
    /*!
     * @brief 配列の指定されたインデックスのデータを返す
     * 
     * 配列オーバーのチェックはしていない
     * 
     * @param index 配列のインデックス
     * 
     * @return 指定されたインデックスへの参照
     */
    __inline T& operator[]( int index ) const{
        return get()[ index ];
    }
    
    /*!
     * @brief Array オブジェクトが持っている、配列へのポインタを返す
     * 
     * @return Array オブジェクトが持っている、配列へのポインタ
     */
    __inline T* get() const{
        return _pObj;
    }
    
    /*!
     * @brief 配列を@a num で初期化する
     * 
     * 配列の実体は、メモリプールから確保される
     * 
     * @param num 配列の要素数
     */
    void set( int num ){
        release();
        
        if( num <= 0 ) return;
        
        length = num;
        // 参照数をカウントするためのカウンタを確保
        _pRef = (uint32*)pool_memory::operator new( sizeof( uint32 ) );
        *_pRef = 1;
        // メモリプールから配列の実体を確保
        _pObj = (T*)pool_memory::operator new( sizeof( T ) * num );
        // コンストラクタ呼び出し
        for( int i = 0 ; i < length ; i++ ){
            new( &_pObj[ i ] ) T();
        }
    }
    
    /*!
     * @brief 参照数を増加させる
     */
    void set( const Array& s ){
        if( (void*)this != (void*)&s ){
            release();
            _pObj = s._pObj;
            _pRef = s._pRef;
            length = s.length;
            if( _pRef != null ){
                ++(*_pRef);
            }
        }
    }
    
    /*!
     * @brief 参照数を減少させる
     */
    void release(){
        if( _pRef != null && (--(*_pRef)) == 0 ){
            // デストラクタを逆方向から呼び出す
            for( int i = length - 1 ; i >= 0 ; i-- ){
                (&_pObj[ i ])->~T();
            }
            pool_memory::operator delete( _pObj );
            pool_memory::operator delete( _pRef );
        }
        _pObj = null;
        _pRef = null;
        length = 0;
    }
    
    int     length;             //!< 要素数
    
private:
    T*          _pObj;      //!< 配列の実体
    uint32*     _pRef;      //!< 参照カウント数
};
// 2 次元のジャグ配列生成
Array< Array< Hoge > > hogeArrays( 10 );
for( int i = 0 ; i < 10 ; i++ ){
    // i 番目の配列に i 個の Hoge を割り当てる
    hogeArrays[ i ].set( i );
}
// 配列の全ての要素に対して処理を行う
for( int i = 0 ; i < hogeArrays.length ; i++ ){
    for( int j = 0 ; j < hogeArrays[ i ].length ; j++ ){
        hogeArrays[ i ][ j ].something();
    }
}

ぶっちゃけると、もうこれで十分使えます。
あとは容量を減らすために型に依存しない処理をテンプレートの外に出すぐらいです。


ただ、自分の場合は2つの理由でこの配列クラスを使いません。

  1. 外部からポインタをセット出来るようにしたい。どうしても大きな配列になってくるとメモリプールから確保するわけにはいかず、そういったときに配列クラスが使えないのは不便だ。
  2. Array クラスを、BREW Smart Pointer で作った BrewRefObject クラスへキャスト出来るようにしたい。このクラスも BrewRefObject クラスへキャスト出来るようにすることで、配列オブジェクトとそうでないオブジェクトをポリモーフィズム的に扱うことが出来るようになる。

ということで、これらを満たす配列クラスを考えてみます。