当前位置: 首页 > 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.jsqmd.com/news/662139/

相关文章:

  • 掌握LibreCAD多语言界面:技术用户必知的20+语言配置指南
  • 【踩坑实录】前端开发必看:一次由CSS缓存引发的线上事故与SEO反思
  • AUTOSAR实战入门:从标准文档到软件架构的探索之路
  • 8个网盘同时限速?这个开源工具让你的下载速度重回巅峰
  • 文档预览革命:用原生Office组件实现秒级文件查看的深度解析
  • 从汽车设计到投资组合:NSGA-II算法在5个真实场景中的建模实战与调参心得
  • 从业务权衡到代码实现:深入理解AUC与ROC曲线的评估逻辑
  • Linux:网络性能调优与 iperf3 实战指南
  • Comics Downloader:构建个人数字漫画图书馆的技术实践
  • 不止于精度图:用EVO的进阶功能深入分析你的SLAM算法表现
  • Figma中文界面终极指南:3分钟让英文设计工具变母语体验
  • 微信小程序Canvas实战:5分钟实现图片自由拖拽+缩放旋转(附完整代码)
  • 2026浏阳周末焰火秀第5场“勇敢追梦不止72变“逗逗烟花嘉年华
  • ShiroExp终极指南:一站式Shiro漏洞检测与渗透测试工具
  • 别再死记硬背了!用PyTorch代码和Tensor图解,5分钟搞懂BatchNorm、LayerNorm和GroupNorm的区别
  • 从庞加莱球到知识图谱:双曲空间中的层次关系建模
  • 手写数字识别项目教程
  • 2025届最火的五大降AI率工具解析与推荐
  • 从“稀释“到“置换“:食品工业脱钠技术的工艺适配与工程难点
  • 告别鼠标!用AutoHotKey一键搞定音量调节(附开机自启设置)
  • 讯飞流式语音识别(ASR)的前端实现(实时语音转写大模型)
  • ISP-全链路数据流预览-000005
  • 如何快速获取50+主流编程语言高清图标库
  • 避开LNA设计中的那些“坑”:从噪声系数到阻抗匹配的实战避坑指南
  • 跨平台流媒体下载终极指南:3步掌握N_m3u8DL-RE高效下载技巧
  • ABAP ALV交互进阶:详解双击事件与动态跳转逻辑
  • Gazebo Sim机器人仿真器:5分钟快速入门完整指南
  • 算法训练营第六天|反转链表
  • [实战][RISC-V]在CH32V407上构建LVGL8.2图形界面:从零开始的移植指南
  • Java继承底层原理:子类到底继承了父类的什么?private成员也能继承?