基类指针调用子类方法时,为何匹配基类参数版本而非子类参数版本?
Method(Component&)而非Method(DerivedComponent&) 问题背景
定义抽象基类Component,包含纯虚方法Method(Component& other);子类DerivedComponent重写该方法,同时重载一个接收DerivedComponent&参数的Method版本。通过Foo类持有指向DerivedComponent实例的Component指针,调用Method时传入另一个Foo的Component实例,为何实际调用的是Method(Component& other)而非Method(DerivedComponent& other)?
相关代码
class Component abstract { public: virtual bool Method(Component& other) = 0; };
class DerivedComponent : public Component { public: virtual bool Method(Component& other) override; virtual bool Method(DerivedComponent& other); };
class Foo { private: Component* m_component; public: Foo() { m_component = new DerivedComponent(); } Component* GetComponent() { return m_component; } };
Foo foo1 = Foo(); Foo foo2 = Foo(); foo1.GetComponent()->Method(*foo2.GetComponent());
原因解析
核心问题出在编译时的重载选择和运行时的多态调用是两个独立的步骤,顺序不能搞反:
编译阶段:编译器只认变量的静态类型
调用foo1.GetComponent()->Method(...)时,GetComponent()返回的是Component*类型的指针——这就是它的静态类型。编译器会基于这个静态类型,去查找Component类里声明的方法,结果只有Method(Component&)这一个选项。至于子类DerivedComponent里新增的Method(DerivedComponent&),编译器在这一步完全看不到,因为基类接口里没有这个方法,自然不会把它加入候选的重载列表。运行阶段:才会根据实际对象类型调用重写方法
确定了要调用Method(Component&)之后,运行时才会根据指针实际指向的DerivedComponent对象,调用子类中重写的Method(Component&)版本,而不是基类的纯虚方法。
总结一下:重载是编译时决定的,编译器只看你用的变量/指针的静态类型;多态是运行时决定的,只负责绑定到对应方法的子类实现。你用基类指针调用方法,编译器只能找到基类里有的那个重载,根本碰不到子类新增的版本。
如果想调用Method(DerivedComponent&),得先把Component*转成DerivedComponent*(比如用dynamic_cast做安全转型),让编译器看到子类的重载方法,才能触发对应的调用。
内容的提问来源于stack exchange,提问作者Isaac Dexter

