BREW Smart Pointer(7)

蛇足その2


実は、今までのスマートポインタは明らかな無駄がある。


Set() の中で Release() しているのだけれども、Release() は、その中で _obj と _ref を null にしている。その後にコピーされるというのに。
副作用のない無駄は除去されるべきなので、その部分を改良する。

// 参照カウントを行うためのクラス
class BrewRefObject{
public:
    ....
protected:
    // オブジェクトをセットします
    void Set( const BrewRefObject& s ){
        if( (void*)this != (void*)&s ){
            InnerRelease();
            _obj = s.get();
            _ref = s.getRefCountDeleter();
            if( _ref != null ){
                _ref->inc();
            }
        }
    }
    
    // 自身の参照数を減らします
    void Release(){
        InnerRelease();
        _obj = null;
        _ref = null;
    }
    
    // 自身の参照数を減らします
    void InnerRelease(){
        if( _ref != null && _ref->dec() == 0 ){
            _ref->Delete( _obj );
            delete _ref;
        }
    }
private:
    ....
};

// スマートポインタクラス
template< class T >
class BrewSmartPtr : public BrewRefObject{
public:
    ....
    // Deleter 付きでポインタをセット
    template< class U , class Deleter >
    void Set( U* p , Deleter ){
        InnerRelease();
        _obj = static_cast< T* >( p );
        _ref = new Deleter();
    }
    ....
};

BrewRefObject の Set() 内にある Release() を InnerRelease() に変えた。これは副作用のない高速化だ。
ただし、BrewSmartPtr クラスの Set() は今まで、

template< class U , class Deleter >
void Set( U* p , Deleter ){
    Release();
    if( p != null ){
        _obj = static_cast< T* >( p );
        _ref = new Deleter();
    }
}

このように書いていた。p に対する null チェックをすることで、無駄な Deleter を生成することを避けていたのだ。
これをそのまま Release() から InnerRelease() へと変えた場合、p が null だったときに、解放されたはずの _obj が null で無くなってしまう。
コードを変更しないことも考えられるけれども、null チェックを省くことによってコードサイズが減少すること、InnerRelease() を使用することによって速度の向上が望めること、そもそも p が null であることはあまり無いということを考えると、InnerRelease() を使用して、そしてnull チェックを省いた方が良いだろう。