ホーム » スタッフ » 斉藤徹 » 講義録 » オブジェクト指向 » 関数ポインタの応用と仮想関数による実装

2018年6月
« 5月    
 12
3456789
10111213141516
17181920212223
24252627282930

最近の投稿(電子情報)

アーカイブ

カテゴリー

関数ポインタの応用と仮想関数による実装

関数ポインタでアルゴリズムの汎用化

関数ポインタを利用すれば、異なるデータに対する処理を、 汎用性高く作ることも可能となる。 例えば以下の vmax() 関数は、自分で用意した大小を比較するだけの関数を渡し、 それ以外の最大値を求めるための処理を行う。 このため、他のデータの最大値を求めたい場合でも、最大値を求める処理を すべて記載するのではなく、対象データの比較関数だけを記述すれば良い。

int intcmp( int* x , int* y ) { // 整数比較関数
   if ( *x > *y )
      return 1 ;
   else if ( *x < *y )
      return -1 ;
   else
      return 0 ;
}
int vmax( void* array ,    // 配列先頭アドレス
          int size ,       // 配列データ件数
          int sizeofdata , // 1件あたりのbyte数
          int(*f)( void*,void* ) ) { // 比較関数
   int i , max = 0 ;
   for( i = 0 ; i < size ; i++ )
      if ( (*f)( array + max * sizeofdata ,
                 array + i   * sizeofdata ) )
         max = i ;
   return max ;
}
int idata[ 4 ] = { 11 , 33 , 22 , 44 } ;
char sdata[ 4 ][ 4 ] = { "ab" , "bc" , "aa" , "c" } ;
void main() {
   int m ;
   // intcmp関数を使って、idata から最大値を探す
   m = vmax( idata , 4 , sizeof(int) , intcmp ) ;
   printf( "%d" , idata[ m ] ) ;
   // strcmp関数を使って、sdata から最大値を探す
   m = vmax( sdata , 4 , sizeof(sdata[0]) , strcmp ) ;
   printf( "%s" , sdata[ m ] ) ;
}

このような、汎用化されたアルゴリズムを処理するプログラムに、関数ポインタを渡す方法では、qsort()が有名である。

純粋仮想基底クラスで書いてみる

class Object {
public:
   virtual int comp( Object* ) = 0 ;
   virtual void print() = 0 ;
} ;

int vmax( Object* array[] , int size ) {
   int max = 0 ;
   for( int i = 0 ; i < size ; i++ ) {
      if ( array[i]->comp( array[m] ) > 0 ) // 仮想関数で比較
         m = a ;
   }
   return m ;
}

class IntObject : public Object {
private:
   int data ;
public:
   IntObject( int x ) : data( x ) {}
   virtual void print() { printf( "%d" , data ) ; }
   virtual int comp( Object* p ) { // static_cast の説明は略
      return data - ((IntObject*)p)->data ;
   }
} ;

int main() {
   Object* array[] = {
      new IntObject( 10 ) , new IntObject( 5 ) , new IntObject( 20 )
   } ;
   array[ vmax( array , 3 ) ]->print() ; // 20
   return 0 ;
}

純粋仮想基底クラスを用いる場合は、純粋仮想基底クラスから、自分の処理のための型を派生させ、自分の型のための仮想関数を少し記述する…

しかし、このような純粋仮想基底クラスと仮想関数を使ったプログラミングは、メモリの使用効率・処理効率も高くないので、最近ではあまり使われない。

若干、オブジェクト指向とは関連が弱いが、テンプレートクラスを紹介する。

テンプレートクラス

C++のテンプレートクラスは、型の部分をテンプレート名として記述し、必要に応じて型を明示する。

// 足し算の処理を様々な型で記述するのは面倒
int add( int a , int b ) {
    return a + b ;
}
double add( double a , double b ) {
    return a + b ;
}
// templateを使って、必要に応じて関数を定義する。
template <typename T>
T add( T a , T b ) {
    return a + b ;
}
template <typename T>
int vmax( T a[] , int size ) {
   int m = 0 ;
   for( int i = 0 ; i 6lt; size ; i++ )
      if ( a[i] > a[m] )
         m = i ;
}
int ia[] = { 1 , 3 , 2 } ;
double fa[] = { 1.3 , 4.5 , 6.2 } ;
int main() {
    printf( "%d¥n" , add<int>( 3 , 4 ) ) ;
    printf( "%ld¥n" , add<double>( 3.0 , 4.0 ) ) ;
    printf( "%d¥n" , vmax<int>( ia , 3 ) ) ;
    printf( "%d¥n" , vmax<double>( faa , 3 ) ) ;
    return 0 ;
}