BREW Smart Pointer(2)
ジョンが書いたスマートポインタは、次のようなプログラムだった。
// 参照カウントとオブジェクト解体を行うクラス 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 ); } } }; // スマートポインタクラス template< class T > class smart_ptr{ public: // デフォルトコンストラクタ smart_ptr() : _obj( null ) , _ref( null ){} // コピーコンストラクタ template< class U > smart_ptr( smart_ptr< U >& 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 U , class Deleter > smart_ptr( U* p , Deleter deleter ) : _obj( null ) , _ref( null ){ Set( p , deleter ); } // デストラクタ ~smart_ptr(){ Release(); } // 代入演算子 template< class U > smart_ptr& operator=( const smart_ptr< U >& s ){ Set( s ); return *this; } // ポインタをセット。Deleter は BrewRefCountDeleter template< class U > void Set( U* p ){ set( p , BrewRefCountDeleter< U >() ); } // Deleter 付きでポインタをセット template< class U , class Deleter > void Set( U* p , Deleter ){ Release(); if( p != null ){ _obj = static_cast< T* >( p ); _ref = new Deleter(); } } // スマートポインタの代入 template< class U > void Set( const smart_ptr< U >& s ){ if( (void*)this != ((void*)&s) ){ Release(); // ここで失敗したら不正なキャストをしようとしたという意味 _obj = static_cast< T* >( 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, アロー演算子、間接参照演算子 T* get() const{ return _obj; } T* operator->() const{ return get(); } T& operator*() const{ return *get(); } // public にするのはあまり良くないんだけど……。 BrewRefCountDeleterBase* getRefCountDeleter() const{ return _ref; } private: T* _obj; BrewRefCountDeleterBase* _ref; };
このスマートポインタはアップキャストやダウンキャストも出来る。ポインタを解放する方法も delete による解放だけでなく、delete[] や IBASE_Release() 等による解放も出来る。必要であれば自分で追加することも可能だ。
彼はこのスマートポインタを使ってプログラムを組んだ。開発効率は飛躍的に上がった。
何度も言うようだが、彼は優秀なプログラマだった。彼の前に勤めていた会社で Java プログラミングを行っていた時期は、彼の設計によるアプリケーションが多くを占めた。それだけ彼の設計能力が高かったということだ。
また、delete を手動で行うという、人間の手による管理の危険性を本能的に察知し、それを回避するために多大な研究時間を費やした。その研究による経験と成果は計り知れないものがあるだろう。
つまり、スマートポインタさえ作ってしまえば、以前の経験と、それからこのスマートポインタを作るために積んだ経験が最大限に生かされるということなのだ。
さて、話を戻そう。このプログラムのどこに問題があるのだろうか。
例えば smart_ptr< A > を作ったとする。するとテンプレートによって smart_ptr< A > というクラスが生成され、そのクラスにはコンストラクタ、デストラクタ、代入演算子、アロー演算子、間接参照演算子、Set()、Release()、get() がある。ついでに言えば Deleter も必要になる。
これでわかっただろうか。
もう少し例を見ていこう。
smart_ptr< B > を作った。テンプレートによって smart_ptr< B > というクラスが生成され、そのクラスにはコンストラクタ、デストラクタ、代入演算子、アロー演算子、間接参照演算子、Set()、Release()、get() がある。ついでに言えば Deleter も必要になる。
B を継承した C というクラスを作り、smart_ptr< C > を作った。テンプレートによって smart_ptr< C > というクラスが生成され、そのクラスにはコンストラクタ、デストラクタ、代入演算子、アロー演算子、間接参照演算子、Set()、Release()、get() がある。ついでに言えば Deleter も必要になる。さらに、smart_ptr< C > を smart_ptr< B > に代入すると、テンプレートによって smart_ptr< B >に、smart_ptr< C > の代入を行うための代入演算子と Set(),Release() が追加される。
ここまで来ればもう分かるだろう。テンプレートを多用することによる弊害。Windows プログラミングでは、その多くは無視されてきた。しかし携帯アプリケーションを開発する上ではこの上なく障害になるもの。それは、バイナリサイズの肥大化だ。
彼はいくつものスマートポインタを作った。その数は優に 100 を超える。そしてそれをコンパイルを行って、最終的なバイナリへと変換したとき、思わず目を疑った。バイナリサイズが大き過ぎる。なんと、バイナリサイズが通常のポインタを使用した場合と比べて倍近くまで膨れあがっていたのだ。
いくつもの試練を乗り越え、必死の思いで頂上へとたどり着いたジョン。しかしそこはカルデラの頂上だった。周りを見渡しても、目線より高くにある外輪山が広がるばかりだった。
井の中の蛙。彼の心は真っ黒に覆われた。しかし、これも何度も言うようだが、彼は努力家なのだ。
彼はカルデラを降りて外輪山へと登る準備を進めた。大丈夫だ。彼は一昔前の、ポインタの概念すら知らない男ではない。今なら出来る。
そして彼は遂に未知の領域に手を付け始める...。(つづく)