Version 17.17
関数ポインタの代わりとしてのポリモーフィズム
「前回は純粋仮想関数と抽象クラスについて説明しました」
『んーでもやっぱり目的がいまいちわかんないかも?』
「というわけで、抽象クラス、というかポリモーフィズムの具体的な使用例
を今回は説明します。比較的分かりやすいのが、関数ポインタとして使うこ
とかな」
『関数ポインタって、関数のアドレスを入れる変数だよね』
「そう、 Version 13.01 ( No.237 ) や Version 13.17 ( No.253 ) で説明
した、関数のアドレスを格納する変数のこと」
『あれと同じような使い方ができるの?』
「そう。ちょうど Version 13.17 ( No.253 ) で使用している qsort() 関数
の比較用関数みたいにね。実際に今回は、ソートの比較用関数を抽象クラス
で実現してみます」
『おー、なんか今回は実践的』
「まず、ベースは Version 13.09 ( No.245 ) の、 CompareTo() 関数と
DoBubbleSortUseComapre() 関数の組み合わせを利用します」
『これをポリモーフィズムに置き換える、と』
「そういうこと。今回は、まず完成品のプログラムから」
// Main.cpp
#include <Windows.h>
#include <stdio.h>
// 比較用クラスの基本クラス。
class CComparetor
{
public:
// 比較用メンバ関数。純粋仮想関数です。
virtual bool CompareTo( int p_iL, int p_iR ) = 0;
};
// 実際の比較用クラスです。
// 整数値を昇順でソートするよう比較します。
class CIntComparetor : public CComparetor
{
public:
// 比較用メンバ関数。
// p_iL > p_iR なら true を、
// それ以外は false を返すようにしてください。
bool CompareTo( int p_iL, int p_iR )
{
if( p_iL > p_iR )
{
return true;
}
return false;
}
};
// 実際の比較用クラスです。
// 整数値を降順でソートするよう比較します。
class CIntDescComparetor : public CComparetor
{
public:
// 比較用メンバ関数。
// p_iL < p_iR なら true を、
// それ以外は false を返すようにしてください。
bool CompareTo( int p_iL, int p_iR )
{
if( p_iL < p_iR )
{
return true;
}
return false;
}
};
// ソートを行う関数です。
void DoBubbleSortUseComapre
( int *p_piAry, int p_iSize, CComparetor *p_pcComparetor )
{
// 「入れ替え先」のループです。
for( int iOut = 0; iOut < p_iSize - 1; iOut++ )
{
// 最後から先頭方向へのループです。
// ただし「入れ替え先」までです。
for( int iIn = p_iSize - 1; iOut < iIn; iIn-- )
{
if( p_pcComparetor->CompareTo
( p_piAry[iIn - 1], p_piAry[iIn] ) == true )
{
// 前の方が大きいので入れ替えます。
int iTemp = p_piAry[iIn];
p_piAry[iIn] = p_piAry[iIn - 1];
p_piAry[iIn - 1] = iTemp;
}
}
}
}
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
int iAry[5];
iAry[0] = 4;
iAry[1] = 9;
iAry[2] = 3;
iAry[3] = 5;
iAry[4] = 8;
// 比較用クラスの変数を用意します(昇順)。
CIntComparetor cIntComparetor;
// ソートします。
DoBubbleSortUseComapre( iAry, 5, &cIntComparetor );
for( int iF1 = 0; iF1 < 5; ++iF1 )
{
char pch[256];
sprintf( pch, "%d ", iAry[iF1] );
OutputDebugString( pch );
}
OutputDebugString( "\n" );
// 3 4 5 8 9
// 比較用クラスの変数を用意します(降順)。
CIntDescComparetor cIntDescComparetor;
// ソートします。
DoBubbleSortUseComapre( iAry, 5, &cIntDescComparetor );
for( iF1 = 0; iF1 < 5; ++iF1 )
{
char pch[256];
sprintf( pch, "%d ", iAry[iF1] );
OutputDebugString( pch );
}
OutputDebugString( "\n" );
// 9 8 5 4 3
return 0;
}
『長!』
「新しいクラス使うこともあって複雑になりそうだったから、 Main.cpp に
まとめてあります」
『おー、メンバ関数もインラインになっててちょっと見やすいかも』
「まずクラス構造は以下のようになっています」
┌───────────┐
│CComparetor クラス │
└───────────┘
△
│
┌───────┴───────┐
│ │
┌─────┴─────┐ ┌──────┴──────┐
│CIntComparetor クラス │ │CIntDescComparetor クラス │
└───────────┘ └─────────────┘
「 CComparetor クラスには純粋仮想関数 CompareTo() メンバ関数がありま
す。ソートを行う DoBubbleSortUseComapre() では、このメンバ関数を呼び
出して比較を行います」
// ソートを行う関数です。
void DoBubbleSortUseComapre
( int *p_piAry, int p_iSize, CComparetor *p_pcComparetor )
{ ↓
// ... ↓
if( p_pcComparetor->CompareTo ←←←←←
( p_piAry[iIn - 1], p_piAry[iIn] ) == true )
// ...
}
「この DoBubbleSortUseComapre() 関数は、元のプログラムのこの比較箇所
だけ変更してあります。それ以外は同じということを確認してください」
『前回と同じ、基本クラスのポインタを使って純粋仮想関数を呼び出すわけ
ね。で、実際に呼び出されるのは、オーバーライドして定義を作っている、
派生クラスの方ってことね』
「そういうこと。実際に呼び出されるのは CIntComparetor クラスと
CIntDescComparetor クラスの CompareTo() メンバ関数です」
『このメンバ関数の中身も、元のプログラムの CompareTo() 関数と同じね。
CIntComparetor クラスと CIntDescComparetor クラスの違いは?』
「昇順と降順。 CIntDescComparetor クラスの方は【<】が【>】になってる
から」
『 Version 13.08 ( No.244 ) の話ね』
「で、こうすることで、どちらのクラスを渡すかによって昇順と降順が切り
替わるようになります」
// 比較用クラスの変数を用意します(昇順)。
CIntComparetor cIntComparetor; →→→→→
↓
// ソートします。 ↓
DoBubbleSortUseComapre( iAry, 5, &cIntComparetor );
// 3 4 5 8 9 ←昇順
// ...
// 比較用クラスの変数を用意します(降順)。
CIntDescComparetor cIntDescComparetor; →→→
↓
// ソートします。 ↓
DoBubbleSortUseComapre( iAry, 5, &cIntDescComparetor );
// 9 8 5 4 3 ←降順
『おお、ちゃんと切り替わってる!』
「と、ちょっと早足すぎたから次回も引き続き」
/*
Preview Next Story!
*/
「この辺が良く使われる方法かな」
『なんかやっと見えてきたかも』
「ま、それも基本的な仕組みが解っているからなんだけどね」
『む、そういうもの?』
「そういうもの」
『というわけで次回』
< Version 17.18 「インターフェイス」という考え方 >
「につづく!」
『いつも通り具体例先の方が良かった気がするけど』
「ポリモーフィズムだけは逆がいいかな、って」