C++ の関数型 (1): 関数ポインタ

クラスや他の関数のパラメーターなど、C++ で関数を渡す方法。
関数を表現する必要がある型があり、いくつかの異なる方法があります。
本来は関数の型を全てまとめようと思っていたのですが、関数ポインタを書き終えたところで、ネタバレが多いので何回かに分けて記事にしようと思います。

関数ポインタ

C 言語から継承されたメソッドは、関数の型を表す最も基本的で不可欠な方法です。
関数はいくつかの連続した命令であり、プログラムがメモリにロードされた後、メモリ アドレスは本質的に関数エントリを表します。
プログラムの実行中に関数が呼び出され、パラメータ スタックとレジスタが準備された後、関数エントリ アドレスにジャンプし、対応するロジックの実行を開始します。
実行が完了したら、スタックをクリーンアップし、データを保存して、呼び出し元に戻ります。

他のメソッドと比較して、関数ポインター型の定義はあまり直感的ではありません。既存の関数に依存しないという利点があり(後述の集中定義方法を参照)、最も直接的な定義方法です。
人間にとってはやや読みにくく、コンパイラにとってはより簡単です。

サンプルコード

上記のサンプル コードについては、次の手順を参照してください。

// 一个例子函数
int func(int a) {
	return a+1;
}

typedef int (*func_type) (int); // typedef定义函数指针类型
using func_type_other = int (*) (int);  // using定义指针类型语法

func_type ptr2 = func; // 给函数指针赋值,指向具体函数,函数名本身就是指针
func_type ptr3 = &func; // 给函数指针赋值,指向具体函数,对函数名去地址操作,效果等同直接用函数名

cout << ptr2(1); // 输出2
cout << ptr3(2); // 输出3

// 函数指针作为参数使用
void call_func(func_type one_func) {
	cout << "call_func: " << one_func(5) << endl; // 输出 6
}

call_func(func); // 直接传入函数名
call_func(ptr2); // 传入函数指针

関数ポインタ定義の基本手順(定義方法1)

1) 最初に関数プロトタイプ宣言を書きます。上記の例は次のとおりです:
int func(int a);
2) 関数名を括弧で囲み、関数名を "*type name" に置き換え、他は変更しません。
入力パラメーターの場合、型のみが提供され、パラメーター名はオプションです。
int (*func_type) (int a);
3) typedef を先頭に追加:
typedef int (*func_type) (int a);

大功告成,func_type就是要定义的函数指针类型,可以定义函数指针的变量或参数。

例証する

a) typedef 宣言では、型の前にアスタリスク * が必要であり、ポインタ型のみが関数名によって表されるアドレス型と一致できます。
b) 関数名 (func) と関数名に対するアドレス演算 (&func) は等価です。
c) ステップの定義では、1) と 2) が順番に並んでおり、3) はどこにでも配置できます。

decltypeで関数ポインタを宣言する(定義方法2)

C++ の新しい decltype を使用すると、同じ型の関数が存在する場合、関数ポインターの定義を簡略化できます。
関数ライブラリを開発している場合、関数なしでインターフェイス型を定義する場合は、上記のメソッドを使用して定義することしかできません。

// 例子函数
int func(int a) {
	return a+1;
}

typedef decltype(func) *func_type_two;
using func_type_thress = decltype(func)*; // 注意末尾星号*,func_type_thress和func_type_two完全等价,只是新旧语法差异

func_type_two ptr4 = func;
// func_type_thress ptr4 = func;
cout << ptr4(6);

// 函数指针作为参数使用
void call_func(func_type one_func) {
	cout << "call_func: " << one_func(5) << endl; // 输出 6
}
call_func(ptr4); // 可以调用,说明不同方式定义的函数指针类型相同

例証する

a) タイプの前のアスタリスク * は必須であり、以前と同じように使用されます。
b) ptr4 を call_func に渡して、異なる方法で定義された関数ポインターの型が同じであることを確認します。
実は関数ポインタに関数名を代入できれば同じ型ということになります。

究極のキラーオートで関数ポインタを宣言する(定義方法3)

わからない場合は、auto を使用して下を覆います。ここでも、既存の参照関数が必要です。パラメータの型を直接定義するのには適していません。
コード処理中に関数ポインターを格納または渡すのに適しています。
書くのは簡単ですが、コードのロジックに慣れていない人にとっては読みにくいです。

// 例子函数
int func(int a) {
	return a+1;
}

auto *ptr5 = func;
auto ptr6 = func;
auto ptr7 = &func; // ptr5, ptr6, ptr7都是同样类型的函数指针

auto &ptr8 = func; // ptr8是函数的引用,编译器内部处理函数引用和指针一样,通过下面的调用可以验证
ptr8(8);
call_func(ptr8);

関数参照、効果は関数ポインタと同等(定義方法4)

// 例子函数
int func(int a) {
	return a+1;
}

auto &ptr8 = func; // ptr8是函数的引用,编译器内部处理函数引用和指针一样,通过下面的调用可以验证
ptr8(8);
call_func(ptr8);

using はテンプレート型ポインタを定義します

この能力は typedef では実現できませんが、テンプレート ライブラリの基本的な能力です。using 構文を使用することをお勧めします。

template <typename T>
using Compare = bool (*) (T&, T&);

template <typename T>
void sort(vector<T> vec, Compare<T> compare);

要約する

さまざまな「シンタックス シュガー」の後続の追加により、定義方法が簡素化されます。最も基本的な定義方法は本質的に同じです。
重要なのは、関数エントリ アドレスを指すポインターの本質を理解することです。その他は、コンパイラーが関数ポインターの型 (入力と出力の型を含む) を識別できるようにすることです。

おすすめ

転載: blog.csdn.net/yinminsumeng/article/details/129232645