当前位置: 首页 > news >正文

C++ 成员函数的指针

指向成员函数的指针(Pointer to Member Function)。


1. 基本语法示例

classData1{public:inta;intgetA()const{returna;}voidsetA(intval){a=val;}};// 声明并初始化一个指向成员函数的指针int(Data1::*fp)()const=&Data1::getA;void(Data1::*fp2)(int)=&Data1::setA;

2. 类型声明的语法拆解

int (Data1::* fp)() const为例:

  • int:成员函数的返回值类型。
  • (Data1::* fp)Data1::*表示“指向Data1的成员”,fp是指针变量名。括号是必需的,因为()的优先级高于*
  • ():函数参数列表(这里是空参数)。
  • const:成员函数的常量限定符(如果有的话必须完全匹配)。

对比普通函数指针:

  • 普通函数指针:int (*p)(double)→ 指向一个参数为double、返回int的函数。
  • 成员函数指针:int (Data1::* p)(double)→ 指向Data1的一个参数为double、返回int的成员函数。

3. 初始化(赋值)

使用&类名::成员函数名获取成员函数的指针:

int(Data1::*fp)()=&Data1::getA;void(Data1::*fp2)(int)=&Data1::setA;

注意

  • 不能写成&getA(缺少类作用域)。
  • 取成员函数的地址时,&可以省略(C++ 允许隐式转换),但建议显式写出。
  • 静态成员函数的指针是普通函数指针,不是成员函数指针。

4. 如何调用(解引用)

必须结合具体对象或对象指针,使用.*->*运算符:

Data1 obj;obj.a=100;int(Data1::*fp)()=&Data1::getA;intvalue=(obj.*fp)();// 等价于 obj.getA()Data1*p=&obj;value=(p->*fp)();// 等价于 p->getA()

关键点

  • 调用时必须加括号(obj.*fp)(),因为.*优先级低于函数调用()
    若写成obj.*fp()会被解析为obj.*(fp()),错误。
  • 可以保存返回值,传递参数同理。

5. 成员函数指针的类型必须精确匹配

包括:

  • 返回值类型
  • 参数类型(个数、顺序、const/引用等)
  • const / volatile / ref-qualifier (C++11 的&&&)
classData1{public:intgetA()const;// const 成员intgetA();// 非 const 成员,重载};int(Data1::*fp1)()const=&Data1::getA;// OK,指向 const 版本int(Data1::*fp2)()=&Data1::getA;// OK,指向非 const 版本// 错误:类型不匹配// int (Data1::* fp3)() = &Data1::getA; // 歧义?实际上编译器会选择非 const 版本,前提是只有一个匹配

如果存在重载,需要显式类型转换或通过上下文消除歧义,通常用static_cast

int(Data1::*fp)()const=static_cast<int(Data1::*)()const>(&Data1::getA);

6. 指向虚函数的成员指针

指向虚函数的指针同样遵循多态行为:

classBase{public:virtualvoidprint(){cout<<"Base\n";}};classDerived:publicBase{public:voidprint()override{cout<<"Derived\n";}};void(Base::*pf)()=&Base::print;Derived d;Base*p=&d;(p->*pf)();// 输出 "Derived",因为虚函数通过指针调用会动态分派

7. 常用类型别名简化

usingGetAFunc=int(Data1::*)()const;usingSetAFunc=void(Data1::*)(int);GetAFunc fp=&Data1::getA;SetAFunc fp2=&Data1::setA;

8. 与数据成员指针的异同

特性数据成员指针int Data1::*成员函数指针int (Data1::*)()
声明语法int Data1::* p;int (Data1::* p)();
初始化&Data1::a&Data1::func
需要对象/指针才能使用obj.*pptr->*p(obj.*p)()(ptr->*p)()
调用/访问时加括号不需要()(直接访问成员)必须加(),且括号位置特殊
支持重载不涉及需要类型转换消除歧义
支持虚函数不涉及支持,调用时动态分派
支持 const/volatile成员本身的 const(指针声明无关)必须与函数限定符完全匹配
底层实现偏移量(字节)通常为“thunk”或函数地址 + 调整量

9. 完整示例

#include<iostream>usingnamespacestd;classData1{public:inta;Data1(intx):a(x){}intgetA()const{returna;}voidsetA(intx){a=x;}voidprint()const{cout<<"a = "<<a<<endl;}};intmain(){// 指向成员函数的指针int(Data1::*getter)()const=&Data1::getA;void(Data1::*setter)(int)=&Data1::setA;void(Data1::*printer)()const=&Data1::print;Data1obj(10);Data1*p=&obj;// 调用cout<<(obj.*getter)()<<endl;// 10(p->*setter)(20);(obj.*printer)();// a = 20return0;}

10. 注意事项与最佳实践

  • 优先级和括号:调用时(obj.*ptr)()不能省略外层的括号。
  • 类型别名:推荐用using简化复杂的成员函数指针类型。
  • 指向静态成员函数:使用普通函数指针,而不是成员函数指针。
  • std::function结合:可以使用std::function<int(const Data1&)>包装成员函数,配合std::bind或 lambda 更现代。
#include<functional>autogetter=std::mem_fn(&Data1::getA);intval=getter(obj);// 更简洁

思考:

  • std::mem_fn的原理与用法
  • 成员指针与模板元编程的结合
  • 成员指针在库设计中的实际案例(如信号槽、属性系统)
http://www.cnnetsun.cn/news/1966363.html

相关文章:

  • 深入ORB_SLAM2多线程:拆解Tracking、LocalMapping、LoopClosing三大核心线程如何协同工作
  • 药品说明书查询系统源码 本地数据库 PHP版本
  • 特斯拉Optimus Gen-3的视觉系统拆解:如何用FSD芯片实现99.2%识别准确率?
  • Linux:网络性能调优与 iperf3 实战指南
  • GNSS定位精度提升:从误差源到高精定位技术演进
  • AGI协作窗口期仅剩18个月?顶尖实验室内部推演:4种不可逆协作模式正在成型,你选对了吗?
  • YgoMaster:构建专属游戏王决斗环境的实战手册
  • Cursor Pro免费激活器:三步解锁AI编程神器的完整指南
  • 如何将中国行政区划数据迁移到MySQL数据库?
  • 51单片机新手必看:用Proteus搞定LM016L液晶显示的保姆级仿真教程
  • 2026 云+AI 架构选型指南:IaaS、PaaS、SaaS、FaaS 到 MaaS 的九大服务模型与云原生实战——IaaS、PaaS、SaaS、FaaS、CaaS、DaaS、MaaS、KaaS
  • MATLAB神经网络拟合工具箱实战:从数据导入到模型部署的完整指南
  • Obsidian Dataview终极指南:5个简单步骤将笔记库变为智能数据库
  • Sora Tasks API 集成与使用指南
  • 告别设备切换烦恼:5分钟掌握Input Leap跨平台键鼠共享
  • HCIA全网可达实验 第二次作业
  • Windows流媒体服务器战略转型:从技术选型到业务价值的颠覆性实践
  • 用GraphQL替代RESTful API,我们得到了什么又失去了什么?
  • FPGA跨时钟域设计避坑指南:从脉冲展宽到电平转换,两种Vivado实现方案怎么选?
  • Windows资源管理器终极美化:ApkShellext2让应用文件一目了然
  • 【AGI医疗应用黄金窗口期】:2024年三大临床落地场景与72小时快速验证路径
  • Vue2项目里用jsmind.js实现可拖拽、可截图的思维导图(附完整代码)
  • 从理论到实践:深度学习驱动的医学图像融合前沿方法与开源实现
  • AGI创造性瓶颈诊断指南(2024全球首份可复现评估协议)
  • 3步终极清理方案:彻底解决Visual Studio卸载残留问题
  • 如何高效获取B站完整评论数据:BilibiliCommentScraper终极指南
  • 构建高效原神数据API:genshin.dev API完全指南
  • 当你的肌肉记忆在游戏间迷失:一个开源脚本如何找回你的手感
  • 从t检验到p值:Pearson相关系数显著性检验的统计逻辑探秘
  • Vivado综合实战:从代码风格到资源映射,精准控制BRAM与LUTRAM