BREW Smart Pointer(3)
スマートポインタはバイナリサイズが肥大化することが分かった。しかし、どうやって容量を減らせばいいのだろうか。
ジョンはしばらく考えた。
なぜスマートポインタはテンプレートなのだろうか。これがテンプレートでなければ容量は増えないのに。
もしスマートポインタがテンプレートでなかったらどうなるのだろう。例えば get() メソッドは、
void* get() const{ return _obj; }
このように void* で返すしかない。
いや、待てよ、そこはどうでもいい。そんなのは利便性の問題であって、スマートポインタがテンプレートでなけらばならないという理由にはならない。
ああ、そうか、Deleter だ。BrewRefCountDeleter, BrewRefCountArrayDeleter。奴はポインタを解放するときに、元の型へキャストしてから解放する。これのおかげで、どんなクラスへキャストしても正常に解放されるのだ。もしテンプレートを使用していなかったら、元の型が何であるか分からないので、void を delete することになる。それはスマートポインタとして最低の欠陥品だろう。
Deleter がテンプレートである必要がある、つまり、Deleter 以外はテンプレートにする必要性は無い、ということだ。
ならば話は簡単だ。こうすればいい。
// 参照カウントとオブジェクト解体を行うクラス class BrewRefCountDeleterBase{ public: BrewRefCountDeleterBase() : _count( 1 ) {}; virtual ~BrewRefCountDeleterBase(){} // オブジェクトを解体する virtual void Delete( void* p ) const = 0; // 参照数を増やす uint32 inc(){ return ++_count; } // 参照数を減らす uint32 dec(){ return --_count; } private: uint32 _count; }; // delete で解体する Deleter template< class T > class BrewRefCountDeleter : public BrewRefCountDeleterBase{ public: virtual void Delete( void* p ) const{ delete static_cast< T* >( p ); } }; // delete で解体する Deleter template< class T > class BrewRefCountArrayDeleter : public BrewRefCountDeleterBase{ public: virtual void Delete( void* p ) const{ delete static_cast< T* >( p ); } }; // IBASE_Release() で解体する Deleter class BrewRefCountShellObjectDeleter : public BrewRefCountDeleterBase{ public: virtual void Delete( void* p ) const{ if( p != null ){ IBASE_Release( (IBase*)p ); } } }; // スマートポインタクラス class smart_ptr{ public: // デフォルトコンストラクタ smart_ptr() : _obj( null ) , _ref( null ){} // コピーコンストラクタ smart_ptr( smart_ptr& s ) : _obj( null ) , _ref( null ){ Set( s ); } // ポインタをセット。Deleter は BrewRefCountDeleter template< class U > explicit smart_ptr( U* p ) : _obj( null ) , _ref( null ){ Set( p ); } // Deleter 付きでポインタをセット template< class Deleter > smart_ptr( void* p , Deleter deleter ) : _obj( null ) , _ref( null ){ Set( p , deleter ); } // デストラクタ ~smart_ptr(){ Release(); } // 代入演算子 smart_ptr& operator=( const smart_ptr& s ){ Set( s ); return *this; } // nullの代入演算子 smart_ptr& operator=( long ){ Release(); return *this; } // ポインタをセット。Deleter は BrewRefCountDeleter template< class U > void Set( U* p ){ Set( p , BrewRefCountDeleter< U >() ); } // Deleter 付きでポインタをセット template< class Deleter > void Set( void* p , Deleter ){ Release(); if( p != null ){ _obj = p; _ref = new Deleter(); } } // スマートポインタの代入 void Set( const smart_ptr& s ){ if( (void*)this != ((void*)&s) ){ Release(); _obj = s.get(); _ref = s.getRefCountDeleter(); if( _ref != null ){ _ref->inc(); } } } // 解放 void Release(){ if( _ref != null && _ref->dec() == 0 ){ _ref->Delete( _obj ); delete _ref; } _obj = null; _ref = null; } // getter, アロー演算子、間接参照演算子 void* get() const{ return _obj; } // T* operator->() const{ return get(); } // T& operator*() const{ return *get(); } // public にするのはあまり良くないんだけど……。 BrewRefCountDeleterBase* getRefCountDeleter() const{ return _ref; } private: void* _obj; BrewRefCountDeleterBase* _ref; };
おお!スマートポインタクラスがテンプレートではなくなった!
前のスマートポインタのバイナリを解析してみると、そのほとんどが Set() と Release() で占められていたのだが、このスマートポインタは、Set( const smart_ptr& ) と Release() は1つずつしかない。テンプレートなのはポインタの Set() のみ。しかしそれも微々たるものだろう。前のスマートポインタと比べると、驚くべきダイエットだ。さながら小錦が織田裕二になったかような変貌ぶりだ。まあ、それは天地が逆さまになってもありえないのだが。
彼はこれを使ってプログラミングをしようとした。が、数秒で諦めた。
これは大きな欠点がある。これをポインタの代替として扱うには少々、いや、かなり無理がある。内部に持っているのが void* だから、アロー演算子と間接参照演算子が使えない。使えるのは get() のみだ。
これをわざわざ目的の型へキャストしながら使わなければならない。
smart_ptr a = smart_ptr( new A ); A* pa = static_cast< A* >( a.get() ); pa->Foo();
面倒だ。スマートポインタは管理を楽にするために存在しているのに、こんな方法でやっているといつかキャストするクラスを間違えそうだ。それにタイプ数も多い。こんなのはやってられない。なんとかしなければ。
彼はまた黙考し始めた。(つづく)