関数ポインタについて説明する前に、コンパイラが関数を認識して呼び出す方法という概念を理解する必要があります。
C / C ++プログラムのコンパイル中、メモリには4つの機能パーティションがあります
。1)コード領域:
関数が格納されます。
2)データ領域:
静的データとグローバル変数が保存されます。
3)ヒープ領域に
はポインタが格納されます。
4)スタック領域に
はローカル変数が格納されます。
関数もメモリに格納されるため、関数は他のデータと同様に、メモリ内の対応するメモリユニットを占有する必要があり、各メモリユニットにはエントリアドレスがあります。したがって、プログラムが操作中に関数を呼び出すとき、対応する関数を実行する前に、まずこの関数のエントリアドレスを見つける必要があると考えるのは難しいことではありません。ポインタを介して関数のエントリアドレスを直接ポイントし、ポインタを介してこの関数を呼び出すこともできます。これは関数ポインタです。
関数ポインタの使用
通常の関数ポインター:
関数ポインターと変数ポインターの使用法は同じではありません。関数ポインターは次のように宣言します。
(関数戻り値タイプ)(ポインター)(関数パラメーターリスト)
サンプルプログラム:
#include <iostream>
using namespace std;
void sayName(string name) {
cout << "I am " << name << endl;
}
int main() {
void (*p)(string name) = &sayName;
p("Bob");
return 0;
}
上記のプログラム操作結果の出力:
I am Bob
関数ポインタが関数を指すようにする場合、対応する関数名を使用して割り当てることができますが、関数名の前にアドレス記号とを追加することは問題ありません(追加することをお勧めします)。
クラスメンバー関数ポインター:関数ポインターがクラス
のパブリックメンバー関数を指すようにする場合、このクラスメンバー関数ポインターが指す関数がどのクラスに属するかを示す必要があるため、宣言の方法も通常の関数ポインターとは異なります。
(関数戻り値タイプ)(クラス名::ポインター)(関数パラメーターリスト) *
サンプルプログラム:
#include <iostream>
using namespace std;
class A {
public:
A(string name):name_(name) {
}
public:
void sayName() {
cout << "I am " << name_ << endl;
}
private:
string name_;
};
int main() {
A a("Alice");
A b("Bob");
void (A::*p)() = &A::sayName;
(a.*p)();
(b.*p)();
return 0;
}
上記のプログラム操作結果の出力:
I am Alice
I am Bob
クラスメンバー関数ポインターを介して呼び出す場合、同じクラスの異なるインスタンスオブジェクトがメンバー関数に対応するため、メンバー関数ポインターが指すこのクラスのオブジェクトを説明する必要があるため、通常の関数ポインターの使用とも異なります。実行結果も異なる場合があります(ほとんどの場合、異なるはずなので、同じである可能性があります)。
呼び出すときの構文は次のとおりです。
(インスタンスオブジェクト名。クラスメンバー関数ポインタ)(引数リスト)
上記のプログラムでは、クラスAのメンバーへのポインターpを定義します。最初にクラスAインスタンスオブジェクトaからpのsayNameを呼び出し、次にクラスAインスタンスオブジェクトbからpのsayNameを呼び出します。
さらに、関数ポインタは多態性もサポートします。
サンプルプログラム:
#include <iostream>
using namespace std;
class A {
public:
A(string name):name_(name) {
}
public:
virtual void sayName() {
cout << "I am " << name_ << endl;
}
protected:
string name_;
};
class B:public A {
public:
B(string name, string number):A(name), number_(number) {
}
public:
void sayName() {
cout << "I am " << name_ << " and ";
cout << "My number is " << number_ << endl;
}
private:
string number_;
};
int main() {
A a("Alice");
A b("Bob");
B c("Sandy", "123456");
void (A::*p)() = &A::sayName;
(a.*p)();
(b.*p)();
(c.*p)();
return 0;
}
上記のプログラム操作結果の出力:
I am Alice
I am Bob
I am Sandy and My number is 123456
上記のプログラムでは、AのsayName関数を仮想関数として定義し、新しく定義されたクラスBがAから継承し、親クラスAとは異なるsayName関数をBに実装します。このとき、クラスAのメンバー関数ポインターはサブクラスBのsayName関数を指すことができ、Bに実装されているsayName関数が呼び出されます。