ホーム < ゲームつくろー! < ツール編


その6 スマートポインタ(v2.25)


 解放処理から解放されるスマートポインタテンプレートです。スマートポインタについて詳しくはクラス構築編のスマートポインタの記事を参考にして下さい。

名前 バージョン 公開日
スマートポインタ 2.25 2010. 3.30

バージョンレポートはこちら

 本バージョンではウィークポインタ機能を追加しました。ウィークポインタはテンプレートクラスで、同型のスマートポインタの持つオブジェクトポインタを参照できます。スマートポインタとの違いは参照先が保障されていない点です。つまり、ポインタ先にオブジェクトが必ずあるとは限りません(ダングリング状態の許容)。ただし通常の生ポインタと違い、isExistメソッドでポインタ先がダングリングであるかをチェックする事ができます。

 ウィークポインタを使用する事により、スマートポインタの潜在的な問題である「循環参照問題」を解決できます。詳しくはクラス構築編「ウィークポインタ」をご覧下さい。

 ウィークポインタテンプレートクラスは同じヘッダーファイル内に定義されています。よって、DixSmartPtr.hをインクルードするだけで使えるようになります。

 本バージョンより、空のスマートポインタの参照カウンタ数は0になりました。空同士をコピーしても参照カウンタ数の増加はありません。この変更はウィークポインタが参照カウンタ数をチェックするために必要になりました。

 v2.24でv2.23にあったwp<T>::IsExistメソッドのバグを修正しました。前バージョンではスマートポインタが削除されているにも関わらずウィークポインタのIsExistがtrueを返す場合がありました。誠にすいません。


○ 使用に関する注意

 スマートポインタをメンバとして保持するクラスは必ず仮想デストラクタを設けて下さい。仮想デストラクタが無い場合、クラスは単純に削除されてしまい、スマートポインタのデストラクタが呼ばれません。これはメモリリークにつながります。もしスマートポインタを使用してメモリリークが発生したら、この可能性を疑ってみて下さい。


○ 定義

名前空間 ヘッダー
Dix DixSmartPtr.h


○ スマートポインタメンバメソッド

公開メンバメソッド 説明 使い方 備考
sp( T* src=NULL, int add = 0 )
sp( const sp<T> &src )
sp( sp<T2> &src )
sp( const int nullval )
sp( T* src, bool isAry, int add=0 )
コンストラクタ sp<int> spInt( new int );
sp<int> spAry( new int[25], true );
スマートポインタには必ずnewで確保したメモリを保持させるようにして下さい。newで確保したポインタを2つのスマートポインタに渡しては絶対にいけません(2重消去になるため)。
配列を格納する場合は第2引数にかならずtrueを入れて下さい!
~sp( void ) デストラクタ - -
T& At( int elem=0 ) 要素を取得 sp<int> spAry( new int[25], true );
int val = spAry( 12 );
配列要素を取得します。NULL及びオーバーランの検出は行っていませんので注意して下さい。
void Clear() クリア sp<int> spObj( new int );
spObj.Clear();
自身をクリアしてNULLスマートポインタにします。spObj = 0と0を代入するのと同じです。
bool DownCast( sp<T2> &src ) ダウンキャスト sp<CParents> spC( new CChild );
sp<CChild> spMe;
spMe.DownCast( spC );
引数のスマートポインタが格納しているポインタを自分のポインタにダウンキャストできる場合はダウンキャストコピーを行ってtureを返します。ダウンキャストに失敗すた場合は何もせずにfalseを返します。
ダウンキャストの配列サポートはありません。
T* GetPtr() ポインタ取得 sp<CObject> spObj( new CObject );
CObject *p = spObj.GetPtr();
保持しているポインタの貸し付けを行います。このポインタ先の実体を操作すると共有するすべてのスマートポインタが影響を受けます。取得したポインタを消してはいけません。
T** GetPtrPtr() ダブルポインタ取得 sp<CObject> spObj( new CObject );
CObject **p = spObj.GetPtrPtr();
保持しているオブジェクトへのダブルポインタを取得します。
unsigned int GetRefNum() 参照カウンタ数を取得 signed int num = spObj.GetRefNum(); 参照カウンタ数を取得します。
unsigned int* GetRefPtr() 参照カウンタへのポインタを取得 unsigned int num = *spObj.GetRefPtr(); 参照カウンタへのポインタを取得します。通常は左記のように間接参照演算子によってその値を直接得ます。取得したポインタを通して値を変更してはいけません。
unsigned int GetWeakCntPtr() ウィークカウンタへのポインタを取得 unsigned int num = *spObj.GetWeakCntPtr(); ウィークカウンタへのポインタを取得します。通常は左記のように間接参照演算子によってその値を直接得ます。取得したポインタを通して値を変更してはいけません。
unsigned int GetWeakNum() ウィークカウンタ数を取得 unsigned int num = spObj.GetWeakNum(); ウィークカウンタ数を取得します。このカウンタ数はスマートポインタの参照カウンタ数+ウィークポインタ数になっています。
bool IsAry() 保持しているポインタが配列? CObject *p2 = new CObject[24];
sp<CObject> spObj2;
spObj2.SetPtr( p2, true );
bool IsAry = spObj2.IsAry();
保持しているポインタが配列参照である場合はtrueを、単一オブジェクトである場合はfalseを返します。
void SetPtr( T* src=NULL, unsigned int add = 0)
void SetPtr( T* src, bool isAry, int add=0 )
オブジェクトポインタを設定 CObject *p = new CObject;
sp<CObject> spObj;
spObj.SetPtr( p );
CObject *p2 = new CObject[24];
sp<CObject> spObj2;
spObj2.SetPtr( p2, true );
newで確保したオブジェクトポインタを格納します。代入と同じ役目をしますので、格納前のポインタに対する参照カウンタが1つ減らされ、新しいポインタの参照カウンタを1とします。
 配列を入れる時には必ず第2引数をtrueにして下さい。
void SwapPtr( sp<T> &src ) ポインタを交換 sp<int> I_Src( new int(0) );
sp<int> I_Dest( new int(1) );
sp<int> I_Dest2 = I_Dest;

I_Src.SwapPtr( I_Dest );
保持しているポインタを丸ごと交換します。これにより実質スマートポインタが指す実体を交換した事になります。
 配列と単一のポインタも交換できます。


演算子 説明 使い方 備考
bool operator !=(T *val)
bool operator !=( sp<T>& val )
比較演算子 sp<int> spInt_Src( new int );
sp<int> spInt_Dest( new int );

if( spInt_Src != spInt_Dest )
  return false;
2つのスマートポインタを比較します。同じ実体を指していなければtrueを、同じ実体を指していればfalseを返します。
T& operator *( void ) 間接参照演算子 sp<CObject> spObj( new CObject );
(*spObj).Func( );
保持しているポインタの先にある実体アドレスを返します。使用法はポインタの間接参照演算子と同じです。NULLを保持している場合はメモリ保護違反になりますので注意してください。
T& operator[]( int elem ) 配列参照演算子 sp<int> spAry( new int[256], true );
int val = spAry[35];
spAry[128] = 12345;

通常の配列参照のように要素を取得します。
T* operator+( int add ) ポインタ演算子 sp<int> spAry( new int[256], true );
spAry[35] = 200;
int *p = spAry + 35;
int val = *p;    // 200
加算方向のポインタ演算を行い、該当するポインタを返します。マイナス方向はサポートしていません。
sp<T>& operator =(const int nullval )
sp<T>& operator =( sp<T> &src )
sp<T>& operator =( sp<T2> &src )
代入演算子 sp<CObject> spSrc( new CObject );
sp<CObject> spDest;

spSrc = spDest;
代入すると自身がそれまで持っていたポインタを放棄し、新しいポインタを保持します。参照カウンタは正しく動作します。NULLを代入すると空スマートポインタとなります。
bool operator ==( T* val )
bool operator ==( sp<T>& val )
比較演算子 sp<int> spInt_Src( new int );
sp<int> spInt_Dest( new int );

if( spInt_Src == spInt_Dest )
  return false;
2つのスマートポインタを比較します。こ同じ実体を指していればtrueを、違う実体を指していればfalseを返します。
T* operator ->( void ) メンバ選択演算子 sp<CObject> spObj( new CObject );
spObj->Func( );
保持しているオブジェクトへのポインタを取得します。通常のポインタに対するメンバ選択演算子と同じです。



○ ウィークポインタメンバメソッド

公開メンバメソッド 説明 使い方 備考
wp( const wp<T> &src )
wp( const int nullval )
wp( sp<T> &src )
wp( sp<T2> &src )
wp( wp<T2> &src )
wp( void )
コンストラクタ sp<int> spInt( new int );

wp<int> wpInt = spInt;
wp<int> wpInt2( spInt );
wp<int> wpIntNull( 0 );
wp<int> wpIntNull2;
ウィークポインタのコンストラクタ引数には同型もしくはキャスト可能なスマートポインタを代入できます。スマートポインタと違い生ポインタは受け付けません。
~wp( void ) デストラクタ - -
T& At( int elem=0 ) 要素を取得 wp<int> wpAry( spAry );
int val = wpAry( 12 );
配列要素を取得します。NULL及びオーバーランの検出は行っていませんので注意して下さい。
void Clear() クリア wp<int> wpObj( spObj );
wpObj.Clear();
自身をクリアしてNULLウィークポインタにします。wpObj = 0と0を代入するのと同じです。
bool DownCast( sp<T2> &src ) スマートポインタダウンキャスト代入 sp<CParent> spC( new CChild );
wp<CChild> wpMe;
wpMe.DownCast( spC );
引数のスマートポインタが格納しているポインタを自分のポインタにダウンキャストできる場合はダウンキャストコピーを行ってtureを返します。ダウンキャストに失敗すた場合は何もせずにfalseを返します。
引数がNULLスマートポインタだった場合は、無条件にNULLウィークポインタになります。
ダウンキャストの配列サポートはありません。
bool DownCast( wp<T2> &src ) ウィークポインタダウンキャスト代入 sp<CParent> spC( new CChild );
wp<CParent> wpC = spC;
wp<CChild> wpMe;
wpMe.DownCast( spC );
引数のウィークポインタが格納しているポインタを自分のポインタにダウンキャストできる場合はダウンキャストコピーを行ってtureを返します。ダウンキャストに失敗すた場合は何もせずにfalseを返します。
引数がNULLウィークポインタだった場合は、自身も無条件にNULLウィークポインタになります。
ダウンキャストの配列サポートはありません。
T* GetPtr() ポインタ取得 sp<CObject> spObj( new CObject );
wp<CObject> wpObj = spObj;
CObject *p = wpObj.GetPtr();
保持しているポインタの貸し付けを行います。このポインタ先の実体を操作すると共有するすべてのスマートポインタが影響を受けます。取得したポインタを消してはいけません。
スマートポインタがすでにオブジェクトを消してしまった場合はNULLが返ります。ウィークポインタを使用する時には必ずIsExistメソッドもしくはこのメソッドの戻り値をチェックして参照先がNULLでないかをチェックする必要があります。

T** GetPtrPtr() ダブルポインタ取得 sp<CObject> spObj( new CObject );
wp<CObject> wpObj = spObj;
CObject **p = spObj.GetPtrPtr();
保持しているオブジェクトへのダブルポインタを取得します。
スマートポインタがすでにオブジェクトを消してしまった場合はNULLが返ります。
unsigned int* GetRefPtr() 参照カウンタへのポインタを取得 unsigned int num = *wpObj.GetRefPtr(); 参照カウンタへのポインタを取得します。通常は左記のように間接参照演算子によってその値を直接得ます。取得したポインタを通して値を変更してはいけません。
sp<T> GetSmartPtr() スマートポインタを取得 wp<int> wpInt = spInt;
sp<int> copy = wpInt.GetSmartPtr();
ウィークポインタからスマートポインタを作成します。ウィークポインタが指すオブジェクトが向こうの場合、NULLスマートポインタが作成されます。
unsigned int GetWeakCntPtr() ウィークカウンタへのポインタを取得 unsigned int num = *wpObj.GetWeakCntPtr(); ウィークカウンタへのポインタを取得します。通常は左記のように間接参照演算子によってその値を直接得ます。取得したポインタを通して値を変更してはいけません。
unsigned int GetWeakNum() ウィークカウンタ数を取得 unsigned int num = wpObj.GetWeakNum(); ウィークカウンタ数を取得します。このカウンタ数はスマートポインタの参照カウンタ数+ウィークポインタ数になっています。
bool IsAry() 保持しているポインタが配列? CObject *p2 = new CObject[24];
sp<CObject> spObj2( p2, true );
wp<CObject> wpObj2 = spObj2;
bool isAry = wpObj2.IsAry();
保持しているポインタが配列参照である場合はtrueを、単一オブジェクトである場合はfalseを返します。
bool IsExist() 保持しているポインタが有効? sp<int> spInt( new int );
wp<int> wpInt = spInt;
bool isExit = wpInt.IsExist(); // true
spInt = 0;
isExist = wpInt.IsExist(); // false
保持しているポインタが有効な場合はtrue、スマートポインタがオブジェクトを削除してしまって参照先が無効になっている場合はfalseを返します。
void SwapPtr( wp<T> &src ) ポインタを交換 sp<int> spSrc( new int(0) );
sp<int> spDest( new int(1) );
wp<int> wpSrc = spSrc;
wp<int> wpDest = spDest;

wpSrc.SwapPtr( wpDest );
// wpSrc及びspSrcの指す先が1になる
保持しているポインタを丸ごと交換します。これにより実質スマートポインタが指す実体を交換した事になります。
 配列と単一のポインタも交換できます。


演算子 説明 使い方 備考
bool operator !=( sp<T>& val )
bool operator !=( wp<T>& val )
比較演算子 sp<int> spSrc( new int );
sp<int> spDest( new int );
wp<int> wpSrc = spSrc:
wp<int> wpDest = spDest;
if( wpInt_Src != wpInt_Dest )
  return false;
自身に対してウィークポインタ及びスマートポインタを比較します。同じ実体を指していなければtrueを、同じ実体を指していればfalseを返します。
T& operator *( void ) 間接参照演算子 sp<CObject> spObj( new CObject );
wp<CObject> wpObj = spObj;
(*wpObj).Func( );
保持しているポインタの先にある実体アドレスを返します。使用法はポインタの間接参照演算子と同じです。NULLを保持している場合はメモリ保護違反になりますので注意してください。
T& operator[]( int elem ) 配列参照演算子 sp<int> spAry( new int[256], true );
wp<int> wpAry = spAry;
int val = wpAry[35];
wpAry[128] = 12345;

通常の配列参照のように要素を取得します。
T* operator+( int add ) ポインタ演算子 sp<int> spAry( new int[256], true );
wp<int> wpAry = spAry;
wpAry[35] = 200;
int *p = wpAry + 35;
int val = *p;    // 200
加算方向のポインタ演算を行い、該当するポインタを返します。マイナス方向はサポートしていません。
wp<T>& operator =( sp<T> &src )
wp<T>& operator =( sp<T2> &src )
wp<T>& operator =( wp<T> &src )
wp<T>& operator =( wp<T2> &src )
代入演算子 sp<CObject> spSrc( new CObject );
wp<CObject> wpSrc;

wpSrc = spSrc;

wp<CObject> wpDest;
wpDest = wpSrc;

wpDest = 0; // NULLウィークポインタ化
代入すると自身がそれまで持っていたポインタを放棄し、新しいポインタを保持します。ウィークカウンタは正しく動作します。NULLを代入すると空ウィークポインタとなります。
bool operator ==( sp<T>& val )
bool operator ==( wp<T>& val )
比較演算子 sp<int> spSrc( new int );
sp<int> spDest( new int );
wp<int> wpSrc = spSrc:
wp<int> wpDest = spDest;

if( wpInt_Src == wpInt_Dest )
  return true;
自身に対してウィークポインタ及びスマートポインタを比較します。こ同じ実体を指していればtrueを、違う実体を指していればfalseを返します。
T* operator ->( void ) メンバ選択演算子 sp<CObject> spObj( new CObject );
wp<CObject> wpObj = spObj;
wpObj->Func( );
保持しているオブジェクトへのポインタを取得します。通常のポインタに対するメンバ選択演算子と同じです。



○ バージョンレポート

v2.24 -> v2.25 (2010. 3. 30)
NULLを右辺に取る比較演算子が曖昧だったのを修正

v2.23 -> v2.24 (2010. 3. 18)
ウィークポインタのIsExistメソッドが参照カウントを見ていなかったバグを修正
あるアドレスを確保していたスマートポインタを一度消した後、内部で同じアドレスが確保された場合に
ウィークポインタにそのスマートポインタが代入されないバグを修正

v2.22 -> v2.23 (2010. 1. 15)
ダウンキャスト代入の際に保護メンバであるm_isAryにアクセスできないコンパイルエラーを修正
ゲッターメソッド群にconst付記

v2.21 -> v2.22 (2010. 1. 8 )
const関数内でポインタ参照できるようにconst付き関数および演算子を追加

v2.20 -> v2.21 (2009. 5. 4 )
ウィークポインタにスマートポインタのアップキャストコンストラクタを追加
ウィークポインタのダウンキャストメソッド内のエラー回避

v2.11 -> v2.20 (2009. 4. 29)
ウィークポインタ機能を追加

v2.10 -> v2.11 (2008. 6. 30)
型キャスト代入時のコンパイルエラーを修正(保護メンバへアクセスしようとしたため)

v2.02 -> v2.10 (2008. 5. 8)
配列をサポート

v2.01 -> v2.02 (2007. 3. 6)
vector等のコンテナに格納できないバグを修正(コピーコンストラクタのミスによる)

v2.00 -> v2.01 (2007. 3. 1)
 同形コピーでコピーコンストラクタが呼ばれない不具合を修正
 明示的アップキャスト及び暗黙的アップキャストでダブルポインタが代入されない不具合を修正

v1.00 -> v2.00 (2007. 2. 11)
SwapPtrメソッドによるポインタ入れ替えのサポート

v1.00
基本機能実装
・登録されたオブジェクトポインタの使用者がいなくなったら自動的にdelete
・通常のポインタ操作演算子「*」「->」「[ ]」が使える。
・異なるオブジェクトポインタが登録された状態でアップキャストができる
・DownCastメソッドによる安全なダウンキャストサポート



○ 旧バージョン

名前 バージョン 公開日
スマートポインタ 2.21 2010. 1.8

 本バージョンではウィークポインタ機能を追加しました。ウィークポインタはテンプレートクラスで、同型のスマートポインタの持つオブジェクトポインタを参照できます。スマートポインタとの違いは参照先が保障されていない点です。つまり、ポインタ先にオブジェクトが必ずあるとは限りません(ダングリング状態の許容)。ただし通常の生ポインタと違い、isExistメソッドでポインタ先がダングリングであるかをチェックする事ができます。

 ウィークポインタを使用する事により、スマートポインタの潜在的な問題である「循環参照問題」を解決できます。詳しくはクラス構築編「ウィークポインタ」をご覧下さい。

 ウィークポインタテンプレートクラスは同じヘッダーファイル内に定義されています。よって、DixSmartPtr.hをインクルードするだけで使えるようになります。

 本バージョンより、空のスマートポインタの参照カウンタ数は0になりました。空同士をコピーしても参照カウンタ数の増加はありません。この変更はウィークポインタが参照カウンタ数をチェックするために必要になりました。

スマートポインタ 2.11 2008. 6.30

 本バージョンではnew演算子によって確保された配列を格納できるようになりました。ただし、配列を格納する場合は必ずコンストラクタの第2引数にtrueを指定して下さい。うっかり忘れるとVS2003以前の場合はメモリリークが起こる可能性があります。VS2005は省略しても上手く行くようです(リーク検出で確認済み)。