在 C++ 中,函數指針(Function Pointer) 是一個特殊的指針變量,它存儲的是函數在內存中的起始地址,而不是數據變量的地址。
1. 基本語法
聲明函數指針的關鍵在於:指針的特徵標(返回類型和參數列表) 必須與它指向的函數完全匹配。
語法模板: 返回類型 (*指針變量名)(參數列表);
示例代碼:
int add(int a, int b) { return a + b; }
// 聲明一個指向返回 int,參數為兩個 int 的函數的指針
int (*funcPtr)(int, int);
// 賦值
funcPtr = add;
// 調用(兩種方式等價)
int result = funcPtr(5, 3); // 像普通函數一樣使用
int result2 = (*funcPtr)(5, 3); // 顯式解引用
示例1:
//函數指針
void add(int num1, int num2) {
printf("num1 + num2 = %d\n", num1 + num2);
}
void sub(int num1, int num2) {
printf("num1 - num2 = %d\n", num1 - num2);
}
// 方法指針,傳兩個數
void operate(void(*method)(int, int), int num1, int num2) {
method(num1, num2);
}
int main() {
// 方法指針怎麼定義? 方法的返回(*方法的名稱)(方法的參數)
// 方法指針怎麼定義? return_type (*pointer_name)(parameter_types);
// 定義一個函數指針
//void (*func_p)(int, int) = add;
//// 通過函數指針調用函數
//func_p(10, 20);
operate(add, 10, 20);
operate(sub, 10, 20);
return 0;
}
輸出:
num1 + num2 = 30
num1 - num2 = -10
示例2:
// 定義回調函數類型
typedef void(*CompressCallback)(int progress);
//void compressData(void(*callback)(int)){
void compressData(CompressCallback callback) { //兩種寫法一致,typedef 更清晰
// 模擬數據壓縮過程
for (int i = 0; i <= 100; i += 20) {
// 模擬一些處理時間
// 調用回調函數,傳遞當前進度
callback(i);
}
}
// 回調函數實現
void onCompressProgress(int progress) {
printf("壓縮的進度是: %d%%\n", progress);
}
int main() {
// 啓動數據壓縮,並傳入回調函數
compressData(onCompressProgress);
return 0;
}
輸出:
壓縮的進度是: 0%
壓縮的進度是: 20%
壓縮的進度是: 40%
壓縮的進度是: 60%
壓縮的進度是: 80%
壓縮的進度是: 100%
2. 為什麼要用函數指針?
函數指針的主要用途是將“邏輯”作為參數傳遞。
- 回調函數 (Callbacks):當某個事件發生時,調用預先指定的函數。
- 實現多態性:在 C 語言或底層 C++ 中,可以用函數指針模擬對象的多態行為。
- 排序與搜索: 例如
std::qsort,你可以傳入不同的比較邏輯。
3. 更現代的替代方案
雖然傳統的函數指針在底層開發中依然重要,但在現代 C++ (C++11 及以後) 中,通常推薦使用更安全、更強大的工具:
|
工具 |
描述 |
|
|
一個通用的函數包裝器,可以持有函數指針、Lambda 表達式、成員函數等。
|
|
Lambda 表達式 |
允許在原地定義匿名函數,通常比單獨寫一個函數更簡潔。
|
|
|
用於簡化複雜的函數指針聲明,提高可讀性。
|
簡化示例:
C++
// 使用 using 別名
using MathOp = int(*)(int, int);
MathOp op = add;
// 使用 std::function (推薦)
#include <functional>
std::function<int(int, int)> modernPtr = add;
4. 注意事項
- 括號必不可少:
int *f(int)聲明的是一個返回int*的函數;而int (*f)(int)聲明的是一個函數指針。 - 類型檢查:函數指針不會進行隱式類型轉換,參數和返回值必須嚴格一致。
- 類成員函數: 指向類成員函數的指針語法更復雜(需要使用
&ClassName::FunctionName),通常建議配合std::bind或 Lambda 使用。