写回调时把成员函数直接赋给函数指针,VS 报 C3867:non-standard syntax; use & to create a pointer to member。改成 &Foo::bar,调用处又报 must use .* or ->*。
成员函数指针和普通函数指针不是一回事。普通指针存代码地址,它还要知道在哪个对象上调用,大小一般也不是 8 字节。
声明和调用的固定写法
声明写成 int (Foo::*p)(int) = &Foo::bar; 括号不能省,省了含义就变成返回函数指针的函数。const 成员函数要写成 int (Foo::*p)() const,普通版本赋不进去。
struct Foo { int bar(int x) { return x + 1; } }; int (Foo::*p)(int) = &Foo::bar; Foo f; int y = (f.*p)(1);调用时 (f.*p)(1) 和 (q->*p)(2) 两边的括号同样不能省,.* 和 ->* 的优先级比函数调用低,写错就是编译错误。
为什么它比普通指针大
单继承、非虚函数的情况,多数编译器用两个机器字存:函数地址和 this 调整量,64 位下一般 16 字节。虚函数存的是虚表里的偏移,多继承还要带调整信息。
别用 memcpy 或 memset 处理这种指针,它不能转成 void*,也不能塞进普通函数指针数组直接调用,类型系统会拦住。
和 std::function 怎么选
要绑定对象,用 std::function<int(int)> f = std::bind(&Foo::bar, &obj, std::placeholders::_1); 或者直接写 lambda 捕获对象,读起来更清楚。std::mem_fn(&Foo::bar) 能把这种指针包成可调用对象,配合算法用。
只在一个类内部存回调,它省一次堆分配;跨模块、要保存状态,std::function 更省心,代价是可能的动态分配。
静态成员函数是例外
static 成员函数没有 this,取地址得到的是普通函数指针 int (*p)(int) = &Foo::bar; 这点常被用来绕开成员指针的复杂语法。虚函数通过成员指针调用仍然多态,走对象的虚表,不是编译期绑定。
选型上,库接口要兼容 C,用普通函数指针加 void 上下文;纯 C++ 内部回调,std::function 加 lambda 最省事;成员函数指针留给对象类型固定、在意开销的场景。