OSDN Git Service

reference_counter のコンストラクタを分割
[gintenlib/gintenlib.git] / gintenlib / reference_counter.hpp
1 #ifndef GINTENLIB_INCLUDED_REFERENCE_COUNTER_HPP_
2 #define GINTENLIB_INCLUDED_REFERENCE_COUNTER_HPP_
3
4 /*
5
6       <gintenlib/reference_counter.hpp>
7
8   reference_counter : boost::intrusive_ptr 用オブジェクト生成
9
10   宣言:
11     template<typename Derived, bool multiplely_successable = false >
12     class reference_counter
13     {
14      public:
15       // 参照を増やす
16       void AddRef() const;
17       // 参照を減らす。ゼロになったら自動解放
18       void Release() const;
19       
20      protected:
21       // 初期化
22       reference_counter();
23       // 初期カウントを指定して初期化
24       reference_counter( int initial_count );
25       // コピーは一応可能にする
26       // カウントは動かない
27       reference_counter( const reference_counter& );
28       // 解放
29       ~reference_counter();
30       
31      private:
32       // 代入は出来ない
33       void operator=( const reference_counter& );
34       
35     };
36     
37     template< typename T, bool b >
38     inline void intrusive_ptr_add_ref( const reference_counter<T, b>* ptr );
39     template< typename T, bool b >
40     inline void intrusive_ptr_release( const reference_counter<T, b>* ptr );
41     
42   使用法:
43     class hoge : public gintenlib::reference_counter<hoge>
44     {
45       // 適当に中身を記述
46     };
47     
48     boost::intrusive_ptr<hoge> p = new hoge;
49     // or gintenlib::com_ptr<hoge> p( new hoge );
50     
51     // 多重継承も出来ます。その場合はテンプレート第二引数に true を渡してください
52     struct base1 : gintenlib::reference_counter<base1, true>
53     {
54       // 中身
55     };
56     struct base2 : gintenlib::reference_counter<base2, true>
57     {
58       // 中身
59     };
60     struct derived : base1, base2
61     {
62       // 中身
63     };
64     
65   機能:
66     使用法を見れば一目瞭然でしょう。テンプレート引数に自身を渡して継承すればOKです。
67     第二テンプレート引数は、多重継承が出来るか否か。trueを渡せば多重継承できるようになります。
68     デフォルトでは false です。この場合、仮想関数テーブルは使用していません。
69     よって、非多重継承版で継承をしたい場合、デストラクタを virtual 指定する必要があります。
70     多重継承版では、そういった気遣いをする必要はありません。が、その分コストが高いです。
71     
72   備考:
73     このクラスは、当初は noncopyable として製作されていました。
74     しかし、派生先でコピーコンストラクタを自動生成してくれないのは面倒なので、コピーに限り可能になっています。
75     この際、参照カウントに関しては、コピー元には無関係に 0 に初期化されます。
76     また代入演算は「 copy して swap 」という例外安全な方法があるので、そちらを推奨する意味で禁止になっています。
77
78 */
79
80 #include <cassert>
81 #include <algorithm>
82
83 namespace gintenlib
84 {
85   // 非仮想バージョン。サイズや速度の面で最適化されますが、多重継承は出来ません
86   template<typename Derived, bool multiplely_successable = false >
87   struct reference_counter
88   {
89     void AddRef() const
90     {
91       using namespace std;
92       assert( count >= 0 );
93       
94       ++count;
95     }
96     void Release() const
97     {
98       using namespace std;
99       assert( count > 0 );
100       
101       if( --count == 0 )
102       {
103         delete static_cast<const Derived*>(this);
104       }
105     }
106     
107     // 参照カウントを取得
108     int use_count() const { return count; }
109     
110    protected:
111     reference_counter() : count(0) {}
112     reference_counter( int initial_count ) : count(initial_count) {}
113     ~reference_counter()
114     {
115       using namespace std;
116       assert( count == 0 );
117     }
118     
119     // コピーは一応可能にする
120     reference_counter( const reference_counter& ) : count(0) {}
121     
122    private:
123     mutable int count;
124     // 代入は出来ない
125     void operator=( const reference_counter& );
126     
127   };
128   
129   // boost::intrusive_ptr 用のインターフェイス
130   // reference_counter<T> は reference_counter<T, false> の意味なので
131   // 仮想継承版とは競合しません
132   template< typename T >
133   inline void intrusive_ptr_add_ref( const reference_counter<T>* ptr )
134   {
135     ptr->AddRef();
136   }
137   template< typename T >
138   inline void intrusive_ptr_release( const reference_counter<T>* ptr )
139   {
140     ptr->Release();
141   }
142   
143   
144   // 仮想継承版のベースクラス
145   struct reference_counter_base
146   {
147     void AddRef() const
148     {
149       using namespace std;
150       assert( count >= 0 );
151       
152       ++count;
153     }
154     void Release() const
155     {
156       using namespace std;
157       assert( count > 0 );
158       
159       if( --count == 0 )
160       {
161         delete this;
162       }
163     }
164     // 参照カウントを取得
165     int use_count() const { return count; }
166     
167    protected:
168     reference_counter_base() : count(0) {}
169     reference_counter_base( int initial_count ) : count(initial_count) {}
170     virtual ~reference_counter_base()
171     {
172       using namespace std;
173       assert( count == 0 );
174     }
175     
176     // コピーは一応可能にする
177     reference_counter_base( const reference_counter_base& ) : count(0) {}
178         
179    private:
180     mutable int count;
181     // 代入は出来ない
182     void operator=( const reference_counter_base& );
183     
184   };
185   
186   // 仮想継承版本体。コストは高いですがダイアモンド継承できます
187   template<typename Derived>
188   struct reference_counter<Derived, true>
189     : virtual reference_counter_base
190   {
191    protected:
192     reference_counter()
193       : reference_counter_base() {}
194     reference_counter( int initial_count = 0 )
195       : reference_counter_base(initial_count) {}
196     virtual ~reference_counter(){}
197     
198   };
199   
200   // 仮想継承版では reference_counter_base に対し add_ref/release を定義することで
201   // 衝突が起こらないように工夫されています
202   inline void intrusive_ptr_add_ref( const reference_counter_base* ptr )
203   {
204     ptr->AddRef();
205   }
206   inline void intrusive_ptr_release( const reference_counter_base* ptr )
207   {
208     ptr->Release();
209   }
210   
211   
212 }   // namespace gintenlib
213
214
215 #endif  // #ifndef GINTENLIB_INCLUDED_REFERENCE_COUNTER_HPP_