pointer to member 是编译器生成的偏移描述符,非内存地址,用于定位类成员;需绑定对象调用,如 (obj.*pmf)(),不支持直接解引用或与 nullptr 比较。

什么是 pointer to member?它不是普通指针
它不是内存地址,不能直接解引用,也不能和 nullptr 比较(C++17 前甚至不能判等)。它是编译器生成的“偏移描述符”,用来定位类内部的成员——尤其是当对象地址不确定时,比如你只有 this 的值、但类型在运行时才确定。
常见错误现象:error: cannot convert 'void (A::*)()' to 'void (*)()' in initialization —— 试图把成员函数指针当成普通函数指针用;或者对 nullptr 取地址再转成 int A::*,结果行为未定义。
-
int A::*指向数据成员:本质是该成员相对于A起始地址的字节偏移(可能为负,比如虚基类) -
void (A::*)()指向成员函数:不一定是地址,可能是 {this 调整量, 函数地址} 的组合(尤其涉及多继承或虚继承时) - 必须配合具体对象实例才能调用:
(obj.*pmf)()或(ptr->*pmf)(),.*和->*是专用运算符
怎么声明和调用成员函数指针?别漏掉括号和作用域
语法容易写错:少一对括号就变成函数声明,多一个 * 就变成指针的指针。核心是「先写函数签名,再在参数列表前加 A::*」。
使用场景:事件分发、反射式调用、状态机跳转(比如 state_map["idle"] = &Machine::on_idle;)。
立即学习“C++免费学习笔记(深入)”;
- 正确声明:
void (A::*pmf)() = &A::foo;(注意&A::foo必须带&,且不能取地址后再转型) - 调用必须绑定对象:
(a.*pmf)();或(pa->*pmf)();,不能写成pmf(); - 参数传递和普通调用一致:
(obj.*pmf)(42, "hello");,括号位置不能错 - const 成员函数需匹配 const 限定:
void (A::*pmf)() const = &A::bar;,否则编译失败
为什么 std::function 更常用?它绕过了什么限制
因为 pointer to member 无法单独存在——它必须依附于某个对象。而 std::function<void></void> 可以捕获对象 + 成员指针,封装成可调用对象,接口统一、支持移动、能存 lambda。
性能影响:std::function 有小对象优化(SBO),多数情况下无额外堆分配;但每次调用比原生 .* 多一次间接跳转(vtable 或函数指针查表)。
- 等价转换:
std::function<void> f = [](A& a) { a.foo(); };</void>或std::function<void> f = &A::foo;</void>(后者 C++17 起支持) - 兼容性更好:可传给接受
std::function的库(如std::thread、信号槽) - 避免多继承下的偏移计算陷阱:编译器自动处理
this调整,你不用关心虚基类偏移是否为负
容易被忽略的坑:虚函数、静态成员、空指针检查
虚函数的 pointer to member 不是虚表索引,而是“最终重载函数”的地址(即实际调用目标),所以它不参与动态绑定——你拿到的是编译期确定的函数,不是运行时决定的。
静态成员函数没有 this,它的指针就是普通函数指针:void (*pf)() = &A::static_foo;,千万别套用 void (A::*)() 语法。
- 不能对空指针解引用:
A* p = nullptr; (p->*pmf)();是未定义行为,哪怕pmf指向的函数没访问this成员 - 成员指针本身可以为
nullptr(C++11 起),但调用前必须检查:if (pmf) (obj.*pmf)(); - 模板推导易出错:
auto pmf = &A::foo;推导出完整类型,但若用于泛型上下文,可能因 const/volatile 限定不匹配而失败
真正麻烦的从来不是语法,而是当你在模板元编程里想把 pointer to member 当作类型参数传递,又想保持它和对象类型的绑定关系时——这时候就得小心对齐、偏移、以及不同 ABI 下的二进制兼容性了。










