Version 17.20
戻り値の型をインターフェイスにする
「前回はインターフェイスの具体的な説明をしました」
『つまり、外の関数を呼び出すための接続点になる、そういうクラスが
インターフェイスってことね』
「そういうこと。で、これをもう少し応用してみます」
『応用?』
「そう、インターフェイスを引数ではなく戻り値で使用します」
『……戻り値? 戻り値の型がインターフェイス、ってこと?』
「そういうこと。まず実際の例で見てみようか」
// Main.cpp
#include <Windows.h>
#include <stdio.h>
// 「出力」を行うクラスのインターフェイス。
class CPrinter
{
public:
// 出力関数。純粋仮想関数です。
virtual void Output( const char *p_pch ) = 0;
};
// 「デバッグ出力」を行うクラス。
class CDebugPrinter : public CPrinter
{
public:
// 出力関数。オーバーライドしています。
void Output( const char *p_pch )
{
// デバッグ出力します。
OutputDebugString( p_pch );
}
};
// 「ダイアログ出力」を行うクラス。
class CDlgPrinter : public CPrinter
{
public:
// 出力関数。オーバーライドしています。
void Output( const char *p_pch )
{
// 標準出力に出力します。
MessageBox( NULL, p_pch, "デバッグ", MB_OK );
}
};
// フラグ用定数値。
const int DEBUG_PRINTER = 0;
const int DLG_PRINTER = 1;
// 「出力」クラスを返す関数。
// 引数が DEBUG_PRINTER なら CDebugPrinter クラスのポインタを、
// DLG_PRINTER なら CDlgPrinter クラスのポインタを返します。
CPrinter *GetPrinterInstance( int p_iFlag )
{
// 両クラスの変数を static 変数として作っておきます。
static CDebugPrinter cDebugPrinter;
static CDlgPrinter cDlgPrinter;
// フラグによって出力を変更します。
if( p_iFlag == DEBUG_PRINTER )
{
return &cDebugPrinter;
}
// else
return &cDlgPrinter;
}
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
// 出力用に、 CPrinter クラスのポインタを受け取ります。
CPrinter *pcPrinter;
// まずデバッグ用を受け取ります。
pcPrinter = GetPrinterInstance( DEBUG_PRINTER );
// 出力します。
pcPrinter->Output( "あいうえお\n" );
// 次にダイアログ用を受け取ります。
pcPrinter = GetPrinterInstance( DLG_PRINTER );
// 出力します。
pcPrinter->Output( "あいうえお\n" );
return 0;
}
「まず、 CPrinter クラスがインターフェイスで、その派生クラスとして
CDebugPrinter クラスと CDlgPrinter クラスがあります」
『図にするとこんな感じ?』
┌───────────┐
│CPrinter クラス │
│・Output() │
│ (純粋仮想関数) │
└───────────┘
△
│
┌───────┴───────┐
│ │
┌─────┴─────┐ ┌──────┴────┐
│CDebugPrinter クラス │ │CDlgPrinter クラス │
│・Output() │ │・Output() │
│ (オーバーライド) │ │ (オーバーライド) │
└───────────┘ └───────────┘
「そうそう、そうなります。 CPrinter クラスに Output() メンバ関数とい
う純粋仮想関数があって、それを各派生クラスでオーバーライドしていま
す」
『 CDebugPrinter クラスでは OutputDebugString() 関数で出力して、
CDlgPrinter クラスでは MessageBox() 関数で出力してるわけね。……
ダイアログで出力することなんてある?』
「デバッグモードじゃなくリリースモードの時とか」
『そか、リリースモードでデバッグ……って変だけど、そういうときに使う
わけね』
「そういうこと。で、このクラスの変数を作ってポインタを返すのが、
GetPrinterInstance() 関数。ここは抜き出して説明します。まずこの関数
の引数に渡すふたつのフラグを用意します」
// フラグ用定数値。
const int DEBUG_PRINTER = 0;
const int DLG_PRINTER = 1;
// 「出力」クラスを返す関数。
// 引数が DEBUG_PRINTER なら CDebugPrinter クラスのポインタを、
// DLG_PRINTER なら CDlgPrinter クラスのポインタを返します。
CPrinter *GetPrinterInstance( int p_iFlag )
↑
この引数に DEBUG_PRINTER か DLG_PRINTER を渡します。
「この関数は、戻り値が CPrinter クラスのポインタになっています」
『インターフェイスのポインタ、っていうことはここがポリモーフィズムす
るってことなんだ』
「そういうこと。この戻り値で返すのは、以下の static 変数のどちらかの
アドレスになります」
// 両クラスの変数を static 変数として作っておきます。
static CDebugPrinter cDebugPrinter;
static CDlgPrinter cDlgPrinter;
『 Version 6.02 ( No.102 ) でやったのだね』
「そう。 static メンバ変数とか static メンバ関数とは全然別だから注意
してね」
『はーい。確か、関数がなくなっても残る変数なんだよね』
「そう、普通の変数は関数から抜けるとなくなるんだけど、 static 変数は
最初に関数が呼ばれたときに作られたら、そのままずっと残ります」
『それって、そのあとでポインタを返しているから?』
// フラグによって出力を変更します。
if( p_iFlag == DEBUG_PRINTER )
{
return &cDebugPrinter;
}
// else
return &cDlgPrinter;
「そう。ここで、引数によってどちらかのポインタを返しているんだけど」
『普通の変数だとなくなっちゃうから、ポインタが使えなくなっちゃうもん
ね』
「 Version 4.13 ( No.063 ) で説明したとおり、アドレスが指す先の変数
がなくなっちゃうとポインタとして意味がないから、 static 変数にして
なくならないようにしているわけです」
『なんか変な気もするけど……』
「それは次回説明するから。で、このように、引数に渡す値によって、返さ
れる変数が変わります」
『 DEBUG_PRINTER を渡すと CDebugPrinter クラスの変数のアドレスが返さ
れて、 DLG_PRINTER を渡すと CDlgPrinter クラスの変数のアドレスが返さ
れるわけね』
「このどちらのクラスも CPrinter クラスの派生クラスだから」
『アドレスを返すとポリモーフィズムするわけね』
「そのアドレスを受け取っているのと使っているのが以下のところ」
// 出力用に、 CPrinter クラスのポインタを受け取ります。
CPrinter *pcPrinter;
// まずデバッグ用を受け取ります。
pcPrinter = GetPrinterInstance( DEBUG_PRINTER );
// 出力します。
pcPrinter->Output( "あいうえお\n" );
// 次にダイアログ用を受け取ります。
pcPrinter = GetPrinterInstance( DLG_PRINTER );
// 出力します。
pcPrinter->Output( "あいうえお\n" );
『つまり、 pcPrinter には cDebugPrinter 変数のアドレスが入ってるとき
もあれば、 cDlgPrinter 変数のアドレスが入ってるときもある、ってわけ
ね』
┌ GetPrinterInstance() ──────────────────┐
│ cDebugPrinter cDlgPrinter │
│ ↓ DEBUG_PRINTER だと DLG_PRINTERだと↓ │
│ ↓ こちらを返す こちらを返す↓ │
│ →→→→→→→→→→→↓←←←←←←←←← │
└────────────── ↓ ─────────────┘
↓
pcPrinter
『こーすると pcPrinter がポリモーフィズムするわけね』
「というわけで次回に続く!」
/*
Preview Next Story!
*/
『インターフェイスを戻り値にすることもできるんだねー』
「この方法が分かるとかなり応用範囲が広がるよ」
『でも static 変数使うのって変じゃない?』
「そう、本当は new を使うんだけどね」
『あ、そういえば!』
「というわけで次回」
< Version 17.21 new / delete とポリモーフィズム >
『につづく!』
「しかしそれがポリモーフィズムの発展を阻害するのです!」
『な、なんですか熱くなって』