C++関数オブジェクトと関数ポインタの違い


C++プログラミング言語では、ポインタの応用など、C言語に通じる機能がたくさんあります.ここでは、関数ポインタに似たC++関数オブジェクトについて説明します.C++関数オブジェクトは関数ポインタではありません.ただし、プログラムコードでは、関数ポインタと同様にカッコを付けて呼び出すことができます.これは入門レベルのエッセイで、関数オブジェクトの定義、使用、および関数ポインタ、メンバー関数ポインタとの関係を話しています.
C++関数オブジェクトは実質的にoperator()--カッコオペレータ--を実現するクラスである.例:

  
  
  
  
  1. class Add  
  2. {  
  3. public:  
  4. int operator()(int a, int b)  
  5. {  
  6. return a + b;  
  7. }  
  8. };  
  9. Add add; //   
  10. cout << add(3,2); // 5 

関数ポインタのバージョンは次のとおりです.

  
  
  
  
  1. int AddFunc(int a, int b)  
  2. {  
  3. return a + b;  
  4. }  
  5. typedef int (*Add) (int a, int b);  
  6. Add add = &AddFunc;  
  7. cout << add(3,2); // 5 

ほほほ、定義方式が違う以外は、使い方は同じです.すべて:

  
  
  
  
  1. cout << add(3,2); 

C++関数オブジェクトと関数ポインタの使い方に違いがない以上、なぜ関数オブジェクトを使うのでしょうか.簡単です.関数オブジェクトは追加データを持ち込むことができますが、ポインタはできません.次に、追加データを使用する例を示します.

  
  
  
  
  1. class less  
  2. {  
  3. public:  
  4. less(int num):n(num){}  
  5. bool operator()(int value)  
  6. {  
  7. return value < n;  
  8. }  
  9. private:  
  10. int n;  
  11. }; 

使用時:

  
  
  
  
  1. less isLess(10);  
  2. cout << isLess(9) << " " << isLess(12); //   1 0 

この例はあまりにもふざけているようだ.

  
  
  
  
  1. const int SIZE = 5;  
  2. int array[SIZE] = { 50, 30, 9, 7, 20};  
  3. //  array 10  
  4. int * pa = std::find_if(array, array + SIZE, less(10)); 
    // pa   9   
  5. //  array 40  
  6. int * pb = std::find_if(array, array + SIZE, less(40)); 
    // pb   30   

ここでC++関数オブジェクトの便利さがわかりますよね?追加データを関数オブジェクトに保存できるのが、関数オブジェクトのメリットです.その弱さも明らかで、関数ポインタのように使われていますが、本当の関数ポインタではありません.関数ポインタを使用する場合は、何もできません.たとえば、関数オブジェクトをqsort関数に渡すことはできません.関数ポインタのみが受け入れられるからです.
1つの関数に関数ポインタも関数オブジェクトも受け入れられるようにするには、テンプレートを使うのが一番便利です.次のようになります.

  
  
  
  
  1. template<typename FUNC> 
  2. int count_n(int* array, int size, FUNC func)  
  3. {  
  4. int count = 0;  
  5. for(int i = 0; i < size; ++i)  
  6. if(func(array[i]))  
  7. count ++;  
  8. return count;  

この関数は、配列内の条件を満たすデータの数を統計します.たとえば、次のようにします.

  
  
  
  
  1. const int SIZE = 5;  
  2. int array[SIZE] = { 50, 30, 9, 7, 20};  
  3. cout << count_n(array, SIZE, less(10)); // 2  
  4. :  
  5. bool less10(int v)  
  6. {  
  7. return v < 10;  
  8. }  
  9. cout << count_n(array, SIZE, less10); // 2 

また、C++関数オブジェクトには、クラスメンバーの関数ポインタをカプセル化するために使用できる関数ポインタに匹敵する使い方があります.関数オブジェクトは追加のデータを運ぶことができ、メンバー関数ポインタにはクラスエンティティ(クラスインスタンス)ポインタが欠けているため、クラスエンティティポインタを関数オブジェクトに保存して、対応するクラスエンティティメンバー関数を呼び出すことができます.

  
  
  
  
  1. template<typename O> 
  2. class memfun  
  3. {  
  4. public:  
  5. memfun(void(O::*f)(const char*), O* o): pFunc(f), pObj(o){}  
  6. void operator()(const char* name)  
  7. {  
  8. (pObj->*pFunc)(name);  
  9. }  
  10. private:  
  11. void(O::*pFunc)(const char*);  
  12. O* pObj;  
  13. };  
  14. class A  
  15. {  
  16. public:  
  17. void doIt(const char* name)  
  18. { cout << "Hello " << name << "!";}  
  19. };  
  20. A a;  
  21. memfun<A> call(&A::doIt, &a); //   a::doIt  
  22. call("Kitty"); //   Hello Kitty! 

やっとメンバー関数ポインタを保存して呼び出しに備えることができました.
C++定数参照正しい適用方法C++チェーンスタックテンプレート応用コード解読C++クリップボードの一般的な応用技術共有C++Doxygen実現機能共有C++sprintfフォーマット解決方法詳細しかし、現実は残酷だ.関数オブジェクトは、関数ポインタのように呼び出されるようにメンバー関数ポインタと呼び出し情報を保持することができるが、その能力は限られており、1つの関数オブジェクト定義であり、指定されたパラメータ数のメンバー関数ポインタは最大1つしか実現できない.
標準ライブラリのmem_funはこのような関数オブジェクトですが、0パラメータと1パラメータの2つのメンバー関数ポインタしかサポートできません.int A::func()またはvoid A::func(int)、int A::func(double)など、int A::func(int,double)などのパラメータをもう1つ追加するには、申し訳ありませんが、サポートされていません.欲しいなら、自分で書くしかない.
そして、私たちが自分で書いても、何個書けますか?5個?10個?それとも100個(これも怖い)?
幸いboostライブラリにはboost::functionクラスが提供されており、デフォルトでは10個のパラメータがサポートされており、最大50個の関数パラメータ(多くて、一般的には十分です.しかし、その実現は恐ろしいです.テンプレート部分の特化とマクロ定義で、数十個のテンプレートパラメータを作成し、数十個の関数オブジェクトを偏特化(コンパイル期間)しました.
C++0 xがすでに受け入れられている提案の一つが,可変テンプレートパラメータリストである.この技術を使えば,無数のC++関数オブジェクトを偏特化する必要はなく,関数オブジェクトテンプレート1つで問題を解決できる.