C++言語解説:3-1:関数ポインタ
2002-08-25

[概要] 今まで何気なく使ってきた「関数名」について改めて考えてみます。実は関数名
    もポインタなのです。
    
[構成]・関数ポインタ
     * 関数名の考え方
     * オーバーロード関数へのポインタ


関数ポインタ

 関数名の考え方
 
  ・今まで不思議に感じたことは無いでしょうか。関数を定義した後、関数名を記述す
   るだけで実行できること。プロトタイプ宣言をすれば定義部は後回しに出来ること。
   純粋仮想関数を定義するには「関数名 = 0;」であったこと。
  
  ・特に純粋仮想関数の定義で「おや?」と思う方が多くなるようです。実は関数名も
   ポインタ
なのです。あまりによく使用する機能であるため、簡略化された記述方式
   が採用されており、これが逆に理解の妨げになっています。
  
  ・まずはじめに関数へのポインタを見ることにします。

   [List1.関数ポインタの例]
   ┌───────────────────────────────────┐
    #include <iostream>
    using namespace std;
    
    void message1();
    void message2();
    
    int main()
    {
      void (*p_func)(); //関数ポインタの定義
      int i_select;
      
      cout << "1:Message1, 2:Message2 : ";
      cin >> i_select;
      
      if (i_select == 1) p_func = &message1; //message1()
      else p_func = &message2;        //message2()
      
      (*p_func)(); //関数実行(形式その1)
      
      if (i_select == 1) p_func = &message2; //message2()
      else p_func = &message1;        //message1()

      p_func();  //関数実行(形式その2)
      
      return 0;
    }
    
    void message1()
    {
      cout << "MESSAGE1" << endl;
      return;
    }

    void message2()
    {
      cout << "MESSAGE2" << endl;
      return;
    }
   └───────────────────────────────────┘
   ┌───────────────────────────────────┐
    [出力結果]
     1:Message1, 2:Message2 : 1
     MESSAGE1
     MESSAGE2
   └───────────────────────────────────┘
  
  ・List1にある
     void (*p_func)();
   が関数ポインタの宣言です。void戻値の引数無し型関数へのポインタを宣言してい
   ます。
   
  ・引数や戻り値があるタイプの関数ポインタを宣言する場合は
   ┌───────────────────────────────────┐
     double (*p_func)(int, double);
     戻値型      引数型リスト
   └───────────────────────────────────┘
   のように記述します。
  
  ・関数のアドレス取得は、通常のオブジェクトを扱う場合と同様で
     p_func = &message1;
   のように&演算子を使用します。
  
  ・ポインタを介した関数の実行は
     (*p_func)();
   として行います。*演算子により、ポインタの指すアドレスを参照させています。そ
   して後ろの()は引数です。今回は引数無し関数を扱っているので、()のみです。
  
  ・そしてもう一つ
     p_func();
   まるで関数名のように記述することも可能です。ここである実験をしてみます。
  
   [List2.関数ポインタの例--ポインタ形式記述]
   ┌───────────────────────────────────┐
    #include <iostream>
    using namespace std;
    
    void message1();
    void message2();
    
    int main()
    {
      int i_select;
      
      cout << "1:Message1, 2:Message2 : ";
      cin >> i_select;
      
      if (i_select == 1) message1(); //message1()
      else message2();        //message2()
      
      if (i_select == 1) (*message2)(); //message2ポインタ記述
      else (*message1)();        //message1ポインタ記述

      return 0;
    }
    
    void message1()
    {
      cout << "MESSAGE1" << endl;
      return;
    }

    void message2()
    {
      cout << "MESSAGE2" << endl;
      return;
    }
   └───────────────────────────────────┘
   ┌───────────────────────────────────┐
    [出力結果]
     1:Message1, 2:Message2 : 1
     MESSAGE1
     MESSAGE2
   └───────────────────────────────────┘
  
  ・そうです。いきなり関数ポインタ記述ができてしまいました。これでわかったと思
   いますが、関数名は式中で利用される場合、自動的に関数へのポインタへ変換され
   ている
のです。
  
  ・この処理の仕方は「配列名」に似ています。恐らく同じ処理系統にするため、この
   ような方法が取られたと思うのですが、逆に混乱を招いている気がします。


 オーバーロード関数へのポインタ
 
  ・オーバーロード関数へのアドレスを取得する場合関数ポインタについてもそれぞ
   れの型を用意
します。
  
  ・例えば
     int CalFunc(int i_a);
     int CalFunc(int i_a, int i_b);
   へ適用する関数ポインタは
     int (*p_func1)(int);
     int (*p_func2)(int, int);
   となります。
  
  ・アドレスの代入は今までと同様に&演算子を用います。この際、ポインタの型定義に
   沿ったオーバーロード関数が適用
されます。
     p_func1 = &CalFunc; //CalFunc(int)
     p_func2 = &CalFunc; //CalFunc(int, int)
     
  ・それでは、List3にオーバーロード関数ポインタの例を示します。
  
   [List3.オーバーロード関数へのポインタ]
   ┌───────────────────────────────────┐
    #include <iostream>
    using namespace std;
    
    int CalFunc(int i_a);
    int CalFunc(int i_a, int i_b);
    
    int main()
    {
      int (*p_func1)(int);
      int (*p_func2)(int, int);
      
      p_func1 = &CalFunc; //CalFunc(int)
      p_func2 = &CalFunc; //CalFunc(int, int)
      
      cout << (*p_func1)(10) << endl;
      cout << (*p_func2)(10, 20) << endl;
      
      return 0;
    }
    
    int CalFunc(int i_a)
    {
      int tmp;
      tmp = 2 * i_a;
      
      return tmp;
    }
    
    int CalFunc(int i_a, int i_b)
    {
      int tmp;
      tmp = 2 * (i_a + i_b);
      
      return tmp;
    }
   └───────────────────────────────────┘
   ┌───────────────────────────────────┐
    [出力結果]
     20
     60
   └───────────────────────────────────┘


[Revision Table]
 |Revision |Date    |Comments
 |----------|-----------|-----------------------------------------------------
 |1.00   |2002-08-25 |初版
[end]
Copyright(C) 2002 Altmo