类型转换与虚函数疑问:基类指针为何调用派生类重写的Show函数
为什么强制转换为基类指针后,调用虚函数还是执行派生类版本?
这是个非常典型的问题,刚好戳中了C++虚函数实现动态多态的核心逻辑,咱们一步步拆解清楚:
1. 先搞懂两个关键概念:静态类型 vs 动态类型
- 静态类型:就是你声明指针/引用时写的类型,比如你的代码里
clientesB的静态类型是Base*——编译器编译时能直接识别的类型。 - 动态类型:指针/引用实际指向的对象的真实类型,这里
clientesB指向的是一个Derived对象,所以它的动态类型是Derived*——只有程序运行时才能确定的实际类型。
2. 虚函数的调用规则:看动态类型,不是静态类型
C++的虚函数机制就是为了实现运行时多态:当你通过指针或引用调用虚函数时,程序会根据指针/引用的动态类型(也就是实际指向的对象类型)来决定调用哪个版本的函数,而非静态类型。
背后的实现依赖**虚表(vtable)**机制:
- 每个包含虚函数的类都会生成一张虚表,里面存储着该类所有虚函数的地址。
- 每个对象内部会有一个隐藏的虚表指针(vptr),指向自身所属类的虚表。
- 调用虚函数时,程序会通过对象的vptr找到对应的虚表,再从虚表里取出正确的函数地址执行。
3. 你的代码里发生了什么?
看这段核心代码:
Derived clientsD = Derived(" Rui Pedro", 2000, 50.00); Derived* PclientsD = &clientsD; Base* clientesB =(Base*) PclientsD; // C风格强制转换 clientesB -> Show();
这里的C风格强制转换(Base*)只做了一件事:把Derived*类型的指针值,转换成了Base*类型的指针(也就是改变了指针的静态类型)。但它完全没有修改clientsD这个对象本身——对象依然是Derived类型,内部的vptr依然指向Derived类的虚表。
所以当你用clientesB调用Show()时,程序还是会顺着对象的vptr找到Derived类的虚表,执行Derived::Show()。
4. 怎么才能调用基类的Show()?
如果确实需要调用基类版本的虚函数,可以显式指定作用域,绕过动态绑定:
clientesB->Base::Show();
这样编译器就会直接调用Base类的Show(),而不会去查虚表。
另外提个小建议:C里尽量别用C风格的强制转换,对于公有继承的向上转换(Derived*转Base*),其实不需要强制转换——编译器会自动做隐式转换;如果是其他场景,优先用static_cast/dynamic_cast这类更安全的C类型转换方式。
内容的提问来源于stack exchange,提问作者Pedro R.
相关产品推荐
相关产品推荐

