成员函数指针的工作原理是什么?含普通与虚成员函数场景分析
成员函数指针到底存的啥?
你说的完全对——普通函数指针就是直接存目标函数的起始内存地址,调用的时候跳过去就行,简单直接。但成员函数指针的情况可就复杂多了,毕竟它得同时搞定普通成员函数和虚函数两种完全不同的调用逻辑,具体存啥得看它指向的是哪种函数:
1. 指向普通非虚成员函数的指针
其实编译器处理普通非虚成员函数时,暗地里把它改成了带隐式this指针作为第一个参数的普通函数。那对应的成员函数指针里存的东西,就是这个“被改造后的普通函数”的起始地址,再加上一点点额外信息:这个函数属于哪个类(用来做类型安全检查,比如防止你把Derived类的成员函数指针乱赋值给Base类的指针,除非是合法的继承关系)。
说白了,它存的就是俩核心内容:
- 目标函数的实际内存地址
- 所属类的类型标识(用来确保类型合法)
2. 指向虚函数的成员函数指针
虚函数就不一样了,因为虚函数的实际调用地址是运行时通过对象的虚表(vtable)找的,编译期根本不知道具体要调用哪个(比如子类重写了的话,调用的就是子类的版本)。所以这种成员函数指针存的不是直接的函数地址,而是虚表中的偏移量——意思就是:调用的时候,先找到当前对象的vptr(虚表指针),然后根据这个偏移量去vtable里找对应的函数地址,再结合this指针执行。
不少编译器还会在成员函数指针里加个标记位,用来区分它是指向普通成员函数还是虚函数,这样调用的时候就能走不同的逻辑:
- 如果是普通成员函数:直接用存的地址+
this指针调用就行 - 如果是虚函数:先取对象的vptr找vtable,用偏移量定位函数地址,再调用
给你整个直观的例子
假设我们有这么个类结构:
class Base { public: void non_virtual_func() {} virtual void virtual_func() {} }; class Derived : public Base { public: void virtual_func() override {} };
- 当你拿
&Base::non_virtual_func的时候,这个指针里存的就是non_virtual_func的实际地址,再加上Base类的类型信息 - 当你拿
&Base::virtual_func的时候,这个指针里存的是Base类vtable中virtual_func的偏移量,还有个标记说这是虚函数调用
调用逻辑也不一样:
- 非虚成员函数指针:
obj.*func_ptr()其实就相当于调用non_virtual_func(&obj) - 虚成员函数指针:
obj.*func_ptr()是先取obj的vptr,按偏移量找函数地址,再调用func_addr(&obj)
内容的提问来源于stack exchange,提问作者doron
相关产品推荐
相关产品推荐

