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 チェックを省いた方が良いだろう。