成员函数指针必须绑定类作用域,声明格式为“返回类型 (类名::) (参数列表)”,括号不可省略,且必须通过对象配合.或指针配合->*调用,不能直接调用。

成员函数指针的声明和类型写法很反直觉
普通函数指针是 void (*)(),但类成员函数指针必须绑定类作用域,语法带 ::*,且不能脱离对象单独调用。比如 void (MyClass::*)() 才是合法类型,漏掉括号或写成 void MyClass::*() 会编译失败。
常见错误现象:error: cannot declare pointer to member function 或 expected '(' before '*' token,基本都是括号位置错了。
- 必须写成
返回类型 (类名::*) (参数列表),括号不能省 - 静态成员函数不用这样——它本质是普通函数,可用
void (*)() - const 成员函数要显式加
const,类型变成void (MyClass::*)() const
调用时必须用对象(或指针)配合 .* 或 ->*
成员函数指针不能像普通函数指针那样直接加 () 调用,必须通过对象实例或指针,用 .*(对对象)或 ->*(对指针)操作符组合使用。
示例:
struct A {
void foo() { }
};
A a;
void (A::*p)() = &A::foo;
(a.*p)(); // ✅ 正确:对象 + .*
A* pa = &a;
(pa->*p)(); // ✅ 正确:指针 + ->*
- 写成
p()或(*p)()直接报错:没有隐式对象上下文 - 如果用智能指针(如
std::unique_ptr<a></a>),得先解引用再->*:(ptr->*p)() - 传参方式和普通调用一致,比如
(a.*p)(1, "hello")
用 std::function 包装更安全、更通用
原生成员函数指针语法硬、类型严格、不能默认捕获 this,而 std::function 可以统一处理对象绑定,也支持 lambda 封装,实际项目中更推荐。
- 绑定对象:
std::function<void> f = std::bind(&A::foo, &a)</void> - lambda 更直观:
std::function<void> f = [&a]() { a.foo(); }</void> - 避免生命周期问题:若绑定的是局部对象指针,
std::function不会自动延长其生存期,这点仍需手动保证 - 性能上略慢于原生指针调用(一次虚调用开销),但绝大多数场景可忽略
在模板或容器里存成员函数指针要小心类型匹配
不同类、不同 const 性、不同参数的成员函数指针类型互不兼容,无法放进同一个 std::vector 或作为同一模板参数推导。
比如 void (A::*)() 和 void (B::*)() 是完全不同的类型,哪怕函数签名一样。
- 想泛化存储,得用类型擦除手段,比如
std::function或自定义基类 + virtual call - 模板函数接受成员函数指针时,必须把类类型也作为模板参数:
template<typename t typename r typename... args> void bind(R (T::*)(Args...))</typename> - 注意 MSVC 和 GCC 对空基类优化、this 调整的差异,在多重继承下成员函数指针可能比普通指针大(如 16 字节),别假设它是 8 字节
真正难的不是语法,而是搞清“它必须依附于某个对象实例”这个前提——一旦忘了这点,所有调用都会崩。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











