Version 17.26
スマートポインタの自動解放
「それでは前回の続き。前回は、」
CSmartPointer cSmartPointer = GetPrinterInstance( DEBUG_PRINTER );
「この右側の GetPrinterInstance() 関数が、 return で返す変数に置き換
わって以下のようになって、」
return cSmartPointer;
↓
CSmartPointer cSmartPointer = cSmartPointer;
(左) (右)
『この時は、ふたつの cSmartPointer がひとつの CDebugPrinter クラスの
変数のアドレスを持ってるんだよね』
「そう、こうなります」
┌ cSmartPointer(右)──┐ ┌ CDebugPrinter クラスの変数 ─┐
│m_pcPrinter →→→→→→→→→→│Output() │
│m_piRefCounter →→→→→→ └───────────────┘
└───────────┘ ↓ ┌ int 型変数 ┐ ↑
↑1つめ。 →→→│ 2 ←増えた │ ↑
↓2つめ。 ↑ └──────┘ ↑
┌ cSmartPointer(左)──┐ ↑ ↑
│m_piRefCounter →→→→→→ ↑
│m_pcPrinter →→→→→→→→→→→→→→→→→→→→→→
└───────────┘
「そのあと、右側の cSmartPointer は関数の戻り値なのでなくなるため、
以下のようになります」
┌ CDebugPrinter クラスの変数 ─┐
(なくなった) │Output() │
└───────────────┘
┌ int 型変数 ┐ ↑
→→→│ 1 │ ↑
↑ └──────┘ ↑
┌ cSmartPointer(左)──┐ ↑ ↑
│m_piRefCounter →→→→→→ ↑
│m_pcPrinter →→→→→→→→→→→→→→→→→→→→→→
└───────────┘
「ここまでが以下の行」
CSmartPointer cSmartPointer = GetPrinterInstance( DEBUG_PRINTER );
『この1行にこんな濃い内容が……』
「この cSmartPointer 変数内にある〈CDebugPrinter クラスの変数〉の
アドレスは GetPointer() メンバ関数で取得できるので、以下のように取得
して、そこからメンバ関数を呼び出すことができます」
// 出力します。
cSmartPointer.GetPointer()->Output( "あいうえお\n" );
「ここ、ポリモーフィズムしているからね」
『あ、そか、 Version 17.20 ( No.375 ) のここと同じなんだもんね』
// 出力します。
pcPrinter->Output( "あいうえお\n" );
「 GetPointer() メンバ関数の戻り値は CPrinter クラスのポインタだから」
『だけどアドレスは CDebugPrinter クラスの変数ので、
Output() メンバ関数は仮想関数でオーバーライドされてるから、ってこと
ね』
「そういうこと。こうやって中に保存してあるポインタを取得できること、
ポリモーフィズムされるからこそ、このスマートポインタを使って
Version 17.20 ( No.375 ) と同じことができるんだからね」
『そのためのスマートポインタなんだもんね』
「そういうこと。で、加えてスマートポインタには〈自動的に delete する
機能〉が備わっています」
『それがスマートポインタの目的、なんだよね』
「そう。これからその仕組みを説明します。次の行を見てください」
// 次にダイアログ用を受け取ります。
cSmartPointer = GetPrinterInstance( DLG_PRINTER );
『これって最初に取得したところとほとんど同じね』
「うん、ほとんど同じ。まず GetPrinterInstance() 関数を呼び出します。
ただ、今度は DLG_PRINTER を渡しています」
『そうすると CDlgPrinter クラスの変数を作るんだよね』
「そう、 DLG_PRINTER を渡すことで以下の箇所が実行されます」
// 「出力」クラスを返す関数。
// 引数が DEBUG_PRINTER なら CDebugPrinter クラスのポインタを、
// DLG_PRINTER なら CDlgPrinter クラスのポインタを返します。
CSmartPointer GetPrinterInstance( int p_iFlag )
{
// (略)
// else
CSmartPointer cSmartPointer( new CDlgPrinter() );
return cSmartPointer;
}
『これがスマートポインタの cSmartPointer 変数に渡されて返される、と』
「図にするとこうなります」
┌ cSmartPointer────┐ ┌ CDlgPrinter クラスの変数 ─┐
│m_pcPrinter →→→→→→→→→→│Output() │
│m_piRefCounter →→→→→→ └──────────────┘
└───────────┘ ↓ ┌ int 型変数 ┐
→→→│ 1 │
└──────┘
『これが戻り値で返されると、関数と置き換わるから』
cSmartPointer = GetPrinterInstance( DLG_PRINTER );
『が』
return cSmartPointer;
↓
cSmartPointer = cSmartPointer;
(左) (右)
『って置き換わるんだよね』
「そういうこと。で、ここで重要なのは、左がすでに作られている
cSmartPointer 変数だっていうこと。さっきの部分と比較してみようか」
↓変数を宣言している(新しく作っている)
CSmartPointer cSmartPointer = GetPrinterInstance( DEBUG_PRINTER );
...
↓すでに存在している
cSmartPointer = GetPrinterInstance( DLG_PRINTER );
「そして、 この cSmartPointer は〈CDebugPrinter クラスの変数〉の
アドレスを持ってる、ってことを思い出して」
『そだ、ってことは……』
CDebugPrinter クラスの変数のアドレスを持つ
↓ ↓CDlgPrinter クラスの変数のアドレスを持つ
cSmartPointer = cSmartPointer;
(左) (右)
「つまり、この左と右とでは、持っているアドレスが違うわけです」
『そうなると……?』
「左が持っているアドレスが、右が持っているアドレスで置き換わります。
具体的には = 演算子のオーバーロードメンバ関数が呼び出されます」
// = 演算子をオーバーロードします。
CSmartPointer &operator =( const CSmartPointer &p_rcSmartPointer )
{
if( m_pcPrinter != p_rcSmartPointer.m_pcPrinter )
{
// 対象ポインタのアドレスが違う場合は
// 参照カウンターを減らします。
Release();
}
// コピーします。
m_pcPrinter = p_rcSmartPointer.m_pcPrinter;
m_piRefCounter = p_rcSmartPointer.m_piRefCounter;
// 参照カウンターを増やします。
AddRef();
return *this;
}
『うぉ、こんなの用意してあったんだ』
「このメンバ関数は、まず左右のアドレスを比較して、異なる場合には
Release() メンバ関数を呼び出して参照カウンターを減らします」
『ここではアドレスが違うから呼び出されるね』
「そこで、 Release() メンバ関数をもう一度見てみます」
// 参照カウンターを 1 減らし、ゼロなら解放します。
void Release()
{
// 参照カウンターをひとつ減らします。
--( *m_piRefCounter );
// 参照カウンターがゼロになったら解放します。
if( *m_piRefCounter == 0 )
{
delete m_pcPrinter;
delete m_piRefCounter;
}
}
「ここで参照カウンターを 1 減らします。すると、先ほどの図だと以下の
ようになります」
┌ CDebugPrinter クラスの変数 ─┐
│Output() │
└───────────────┘
┌ int 型変数 ┐ ↑
→→→│ 0←減った │ ↑
↑ └──────┘ ↑
┌ cSmartPointer(左)──┐ ↑ ↑
│m_piRefCounter →→→→→→ ↑
│m_pcPrinter →→→→→→→→→→→→→→→→→→→→→→
└───────────┘
『あ、参照カウンターが0になった!! ってことは……』
「その次の if の条件に合うから、 delete されます」
delete m_pcPrinter;
delete m_piRefCounter;
「つまり、このふたつの変数が delete されるというわけです!」
┌ CDebugPrinter クラスの変数 ─┐
→ │Output() │
両方の変数が └───────────────┘
deleteされます ┌ int 型変数 ┐ ↑
→ →→→│ 0 │ ↑
↑ └──────┘ ↑
┌ cSmartPointer(左)──┐ ↑ ↑
│m_piRefCounter →→→→→→ ↑
│m_pcPrinter →→→→→→→→→→→→→→→→→→→→→→
└───────────┘
『……ってことは、つまりこれが〈スマートポインタが自動的に delete
してくれた〉ってこと……?』
「そういうこと! というわけで次回に続く!」
/*
Preview Next Story!
*/
『や、やっとスマートポインタの目的まで来たね……』
「この自動解放ができないとスマートポインタの意味がないからね」
『……あれ、でも続く?』
「スマートポインタはもっともっと複雑なんです」
『げ』
「というわけで次回」
< Version 17.27 スマートポインタの自動解放、の続き >
『につづく!』
「スマートポインタはここからがおもしろいんです」
『……もうやめにしない?』