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の問題は難しいとか言ったけど、別にそんなに難しい問題でもなかったかも(´・ω・`)