C++ 函数指针
什么是函数指针
在C++中,函数指针是一种特殊类型的指针,它指向函数而不是数据。就像普通指针存储变量的内存地址一样,函数指针存储的是函数的入口地址。这允许我们将函数作为参数传递给其他函数,或者在程序运行时动态选择要调用的函数。
备注
函数指针是C++中回调机制的基础,也是实现多态行为的原始方式之一。
函数指针的语法
声明一个函数指针的基本语法如下:
cpp
返回类型 (*指针名称)(参数类型列表);
例如,声明一个指向接受两个整数并返回整数的函数的指针:
cpp
int (*pFunc)(int, int);
与函数声明的区别
函数指针声明看起来与函数声明很相似,但有一个关键区别:函数指针名称需要用括号和星号括起来。比较:
cpp
// 函数声明
int calculateSum(int a, int b);
// 函数指针声明
int (*pCalculate)(int a, int b);
函数指针的使用
1. 将函数指针指向函数
cpp
#include <iostream>
using namespace std;
// 定义两个函数
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
int main() {
// 声明函数指针
int (*operation)(int, int);
// 将函数指针指向add函数
operation = add;
// 通过函数指针调用函数
cout << "加法结果: " << operation(5, 3) << endl;
// 将函数指针指向subtract函数
operation = subtract;
// 通过函数指针调用函数
cout << "减法结果: " << operation(5, 3) << endl;
return 0;
}
输出结果:
加法结果: 8
减法结果: 2
提示
在C++中,函数名本身就是指向该函数的指针,因此可以直接将函数名赋值给对应的函数指针。
2. 函数指针作为参数
函数指针最强大的用法之一是将其作为参数传递给其他函数:
cpp
#include <iostream>
using namespace std;
// 定义两个函数
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
// 定义一个接受函数指针作为参数的函数
void performOperation(int x, int y, int (*func)(int, int)) {
cout << "结果是: " << func(x, y) << endl;
}
int main() {
performOperation(5, 3, add); // 传递add函数
performOperation(5, 3, multiply); // 传递multiply函数
return 0;
}
输出结果:
结果是: 8
结果是: 15
3. 使用typedef或using简化函数指针声明
函数指针的声明可能比较复杂,可以使用typedef或using来简化:
cpp
// 使用typedef简化
typedef int (*OperationFunc)(int, int);
// 或使用C++11的using别名
using OperationFunc = int (*)(int, int);
// 然后可以这样使用
OperationFunc operation = add;
函数指针数组
函数指针可以组成数组,这在需要存储多个相同类型的函数时非常有用:
cpp
#include <iostream>
using namespace std;
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
int divide(int a, int b) { return b != 0 ? a / b : 0; }
int main() {
// 函数指针数组
int (*operations[4])(int, int) = {add, subtract, multiply, divide};
int a = 10, b = 5;
string opNames[4] = {"加", "减", "乘", "除"};
for (int i = 0; i < 4; i++) {
cout << a << " " << opNames[i] << " " << b << " = " << operations[i](a, b) << endl;
}
return 0;
}
输出结果:
10 加 5 = 15
10 减 5 = 5
10 乘 5 = 50
10 除 5 = 2
实际应用场景
1. 实现回调机制
函数指针常用于实现回调机制,允许底层代码调用高层代码中的函数:
cpp
#include <iostream>
#include <vector>
using namespace std;
// 回调函数类型
typedef void (*ProcessCallback)(int);
// 处理每个元素并调用回调函数
void processElements(vector<int> elements, ProcessCallback callback) {
for (int element : elements) {
callback(element);
}
}
// 回调函数实现
void printSquare(int value) {
cout << value << "的平方是: " << value * value << endl;
}
void printDouble(int value) {
cout << value << "的两倍是: " << value * 2 << endl;
}
int main() {
vector<int> numbers = {1, 2, 3, 4, 5};
cout << "打印平方值:" << endl;
processElements(numbers, printSquare);
cout << "\n打印两倍值:" << endl;
processElements(numbers, printDouble);
return 0;
}
输出结果:
打印平方值:
1的平方是: 1
2的平方是: 4
3的平方是: 9
4的平方是: 16
5的平方是: 25
打印两倍值:
1的两倍是: 2
2的两倍是: 4
3的两倍是: 6
4的两倍是: 8
5的两倍是: 10
2. 命令模式的简单实现
函数指针可以用于实现命令模式,允许将操作封装为对象:
cpp
#include <iostream>
#include <map>
#include <string>
using namespace std;
// 命令处理函数原型
typedef void (*CommandHandler)();
// 命令处理函数
void helpCommand() {
cout << "帮助: 显示所有可用命令" << endl;
}
void exitCommand() {
cout << "退出: 关闭程序" << endl;
}
void versionCommand() {
cout << "版本: v1.0.0" << endl;
}
int main() {
// 创建命令映射表
map<string, CommandHandler> commands;
commands["help"] = helpCommand;
commands["exit"] = exitCommand;
commands["version"] = versionCommand;
// 模拟命令行
string cmd;
while (true) {
cout << "\n输入命令 (help/exit/version): ";
cin >> cmd;
if (cmd == "quit") break;
if (commands.find(cmd) != commands.end()) {
commands[cmd](); // 调用对应的处理函数
} else {
cout << "未知命令。输入'help'查看可用命令。" << endl;
}
}
return 0;
}
警告
在使用函数指针时,必须确保指针指向的函数签名(返回类型和参数列表)与指针类型匹配,否则会导致未定义行为。
与类成员函数的结合
要指向类的成员函数,需要使用特殊的成员函数指针语法:
cpp
#include <iostream>
using namespace std;
class Calculator {
public:
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
};
int main() {
// 声明一个指向Calculator类成员函数的指针
int (Calculator::*operation)(int, int);
// 创建Calculator对象
Calculator calc;
// 指向add成员函数
operation = &Calculator::add;
// 调用成员函数
cout << "加法结果: " << (calc.*operation)(5, 3) << endl;
// 指向subtract成员函数
operation = &Calculator::subtract;
cout << "减法结果: " << (calc.*operation)(5, 3) << endl;
return 0;
}
输出结果:
加法结果: 8
减法结果: 2
函数指针与函数对象和Lambda表达式的比较
现代C++提供了函数对象(functors)和Lambda表达式,这些通常比纯函数指针更灵活:
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool isEven(int num) {
return num % 2 == 0;
}
int main() {
vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 使用函数指针
cout << "使用函数指针找到的第一个偶数: ";
auto result1 = find_if(numbers.begin(), numbers.end(), isEven);
if (result1 != numbers.end()) cout << *result1 << endl;
// 使用函数对象
cout << "使用函数对象找到的第一个大于5的数: ";
struct IsGreaterThan {
int threshold;
IsGreaterThan(int t) : threshold(t) {}
bool operator()(int value) { return value > threshold; }
};
auto result2 = find_if(numbers.begin(), numbers.end(), IsGreaterThan(5));
if (result2 != numbers.end()) cout << *result2 << endl;
// 使用Lambda表达式
cout << "使用Lambda找到的第一个能被3整除的数: ";
auto result3 = find_if(numbers.begin(), numbers.end(),
[](int num) { return num % 3 == 0; });
if (result3 != numbers.end()) cout << *result3 << endl;
return 0;
}
输出结果:
使用函数指针找到的第一个偶数: 2
使用函数对象找到的第一个大于5的数: 6
使用Lambda找到的第一个能被3整除的数: 3
函数指针的优缺点
优点
- 允许运行时选择要调用的函数
- 实现回调机制的基础
- 可以用于实现简单的多态行为
- 比虚函数调用更轻量级
缺点
- 语法较复杂,特别是指向成员函数的指针
- 不能捕获上下文(不像Lambda或函数对象)
- 类型安全性较低
- 现代C++中,通常更推荐使用
std::function
和Lambda表达式
总结
函数指针是C++中一个强大但有时被低估的特性。它们允许我们将函数作为数据传递,实现回调机制,并在运行时动态选择要执行的代码。尽管现代C++提供了更多功能强大的替代品(如std::function
、函数对象和Lambda表达式),但理解函数指针仍然对深入理解C++语言和一些底层机制至关重要。
在实际开发中,函数指针通常用于:
- 与C API交互
- 实现回调系统
- 创建基于表的函数调度
- 插件架构和动态加载模块
练习
- 编写一个程序,使用函数指针数组实现一个简单的计算器,支持加、减、乘、除四种操作。
- 实现一个排序函数,允许通过函数指针参数指定升序或降序排序。
- 创建一个简单的事件处理系统,使用函数指针注册和触发事件。
- 比较函数指针、函数对象和Lambda表达式在实现相同功能时的语法和性能差异。
进一步阅读
- 关于C++11中的
std::function
和Lambda表达式 - 函数指针与C++多态比较
- 使用函数指针实现策略模式
- 在DLL和共享库中使用函数指针