C++函数指针与成员函数指针用法及表驱动实战
C++函数指针分为指向普通函数的指针和指向类非静态成员函数的指针。二者在语法结构上存在显著差异:成员函数指针必须绑定特定类域,且必须显式使用取地址符赋值;调用时需配合对象实例,使用.*或->*运算符。掌握这些特性可有效替代复杂的条件分支语句,通过构建函数映射表提升代码的可扩展性与可维护性。
一、普通函数指针的基础用法
普通函数指针是指向全局函数或命名空间内函数的指针变量。与普通数据指针类似,它指向特定的内存地址(即函数代码入口)。使用函数指针前,必须确保指针声明的类型与目标函数的参数列表及返回类型完全一致。
声明一个普通函数指针的标准语法如下:
int (*pf)(int, int); // 声明一个指向函数的指针,该函数接收两个int,返回int
此处*pf两端的括号至关重要。若省略括号写成int *pf(int, int),编译器会将此标识为一个返回值为int*的函数原型,而非指针变量。
为了避免冗长的重复声明,通常利用typedef定义函数指针类型别名。以下示例展示了定义加法与减法操作对应的函数指针,并作为参数传递调用的过程:
#include <iostream>
#include <string>
using namespace std;
typedef int (*pFun)(int, int); // 定义类型别名
int add(int a, int b){ return a + b; }
int sub(int a, int b){ return a - b; }
// 接受函数指针参数的函数
int calculate(pFun func, int a, int b) {
return (*func)(a, b); // 解引用并调用
}
int main() {
pFun pf = add; // 直接赋值函数名
cout << (*pf)(2, 3) << endl;
// 传入函数指针
cout << calculate(sub, 8, 1) << endl;
return 0;
}
二、类成员函数指针的声明与调用
成员函数指针与普通函数指针的核心区别在于其多态性及所属对象的依赖性。非静态成员函数隐含this指针,因此成员函数指针不仅要描述签名(签名包含参数类型、返回值),还必须锁定所属的类域。同时,成员函数指针无法像普通函数那样自动隐式转换,赋值时必须显式使用取地址符&。
第1步:正确声明与赋值成员函数指针
声明语法要求在指针变量名前加上类名::限定符。例如,针对类A的某个函数:
class A {
public:
int add(int m, int n) { return m + n; }n};
// 声明指向A类成员函数的指针
int (A::*pmf)(int, int);
int main() {
A obj;
// 错误写法:pf = A::add; (编译报错)
// 正确写法:必须显式加取地址符 &
pmf = &A::add;
return 0;
}
第2步:通过对象实例调用成员函数
由于成员函数指针不依附于具体对象,在使用时必须提供对象实例。根据当前获取的是对象还是对象指针,分别使用不同的成员访问运算符:
- 对象调用:使用
(object.*pmf)(args) - 指针调用:使用
(ptr->*pmf)(args)
以下为完整的内部接口封装示例,演示如何在类内部通过this指针调用成员函数指针:
class Calculator {
public:
typedef int (Calculator::*MathFunc)(int, int);
Calculator() {}
// 内部调用方法
int execute(MathFunc func, int a, int b) {
return (this->*func)(a, b); // 使用 this->*
}
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
};
int main() {
Calculator calc;
MathFunc f = &Calculator::add;
cout << calc.execute(f, 10, 5) << endl; // 输出 15
return 0;
}
三、基于Map的函数表驱动实现
在实际开发中,当存在大量相似功能的选择时(如计算器模式、协议解析器等),传统的if...else或switch语句会导致代码臃肿且难以维护。利用关联容器(如std::map)将操作符字符串映射到对应的成员函数指针,可以构建灵活的“函数表”,实现运行时的动态路由。
#include <iostream>
#include <string>
#include <map>
using namespace std;
class TableDrivenCalc {
public:
// 定义成员函数指针类型别名
typedef int (TableDrivenCalc::*Operation)(int, int);
TableDrivenCalc() {
// 构造函数中建立映射关系
ops["+"] = &TableDrivenCalc::add;
ops["-"] = &TableDrivenCalc::sub;
ops["*"] = &TableDrivenCalc::mul;
ops["/"] = &TableDrivenCalc::div;
}
// 统一调用入口
void run(string op, int a, int b) {
auto it = ops.find(op);
if (it != ops.end()) {
Operation func = it->second;
cout << (this->*func)(a, b) << endl;
} else {
cerr << "Unknown operator: " << op << endl;
}
}
private:
map ops;
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int div(int a, int b) { return (b != 0) ? a / b : 0; }
};
int main() {
TableDrivenCalc c;
c.run("+", 10, 3); // 输出 13
c.run("-", 10, 3); // 输出 7
c.run("*", 10, 3); // 输出 30
return 0;
}
这种模式的优点在于,如果需要增加新的计算操作(如平方根),只需添加一个新函数并在映射表中注册一行代码即可,无需修改核心控制逻辑。
四、关键注意事项总结
- 符号优先级:函数调用运算符
()优先级高于解引用和指针访问。调用函数指针时务必加括号:(*pf)(args),调用成员函数指针时为(obj.*pf)(args)。 - 显式取址:成员函数指针赋值时必须写
&Class::Method,不可省略&。 - 空指针检查:若从映射表中查找失败,应确保代码中有处理未知键值的逻辑,防止访问非法内存。
以上就是C++普通函数指针与成员函数指针的详细解析及表驱动应用的详细内容,更多关于C++高级编程技巧的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









