基类能否判断派生类是否重写了某个虚方法?
在C++中判断派生类是否重写了虚函数
嘿,这个问题确实和C#里的处理逻辑不一样——C++没有像C#里MethodInfo.GetBaseDefinition()这类直接的API,得换个思路来实现。咱们先看你的需求:基类Base的dispatch方法需要根据派生类是否重写了newStyle,来决定调用newStyle还是oldStyle,核心就是实现newStyleHasBeenOverridden这个判断方法。
可行的标准实现方案
这里提供一个不依赖编译器私有实现、完全符合C++标准的方法,核心思路是通过一个带检查标记的内部虚函数来判断:
#include <iostream> #include <stdexcept> class Base { private: mutable bool m_usingBaseNewStyle = false; // 内部虚函数,带检查重写的标记参数 virtual void newStyleImpl(int val, bool checkOverride) { if (checkOverride) { // 如果进入基类的这个方法,说明没有被重写 m_usingBaseNewStyle = true; return; } throw std::runtime_error("Implement me!"); } public: void dispatch() { if (newStyleHasBeenOverridden()) { newStyle(42); } else { oldStyle(1, 2); } } virtual void oldStyle(int, int) { throw std::runtime_error("Implement me!"); } // 对外暴露的公共接口,和你原来的签名一致 void newStyle(int val) { newStyleImpl(val, false); } bool newStyleHasBeenOverridden() { m_usingBaseNewStyle = false; // 调用检查版本的虚函数 newStyleImpl(0, true); // 如果标记没被设置,说明进入了派生类的重写版本 return !m_usingBaseNewStyle; } }; class Derived : public Base { private: void newStyleImpl(int val, bool checkOverride) override { if (checkOverride) { // 派生类重写后,不会触发基类的标记设置 return; } std::cout << "Success!" << std::endl; } }; // 测试代码 int main() { Derived d; d.dispatch(); // 输出 "Success!" Base b; try { b.dispatch(); // 会抛出oldStyle的异常 } catch (const std::runtime_error& e) { std::cout << e.what() << std::endl; // 输出 "Implement me!" } return 0; }
方案说明
- 内部虚函数拆分:把原来的
newStyle拆分成对外的公共接口和内部的newStyleImpl虚函数,后者多了一个checkOverride标记参数。 - 标记判断逻辑:当调用
newStyleHasBeenOverridden时,我们传入checkOverride=true调用newStyleImpl:- 如果派生类没重写
newStyleImpl,会进入基类的版本,设置m_usingBaseNewStyle为true,此时返回false(说明没重写)。 - 如果派生类重写了
newStyleImpl,会进入派生类的版本,不会设置基类的标记,此时返回true(说明已重写)。
- 如果派生类没重写
- 保持接口兼容:对外的
newStyle(int)接口和你原来的代码完全一致,派生类只需要重写内部的newStyleImpl即可,逻辑完全贴合你的需求。
为什么不推荐虚表直接操作?
很多人会想到直接读取对象的虚表指针来比较函数地址,但这种方法完全依赖于编译器的虚表布局(比如GCC、MSVC、Clang的虚表结构都有差异),代码不具备可移植性,而且容易因为类的虚函数顺序变化而出错,所以不推荐在生产代码中使用。
内容的提问来源于stack exchange,提问作者Dženan
相关产品推荐
相关产品推荐

