C++如何确保子类重写函数时调用了基类的对应原函数
确保子类重写函数时必须调用基类对应函数的实现方案
有两种成熟的实现方向,分别对应运行时校验和编译期约束,可根据业务场景选择:
方案1:运行时校验方案
适合需要支持动态多态、多层继承的场景,未调用基类函数时会直接抛出异常或触发断言。
核心思路是基类内部维护调用标记,公共调用入口统一校验标记状态:
#include <iostream> #include <stdexcept> class Base { private: mutable bool base_func_called = false; public: virtual void func() { base_func_called = true; std::cout << "Really important code was ran" << std::endl; } // 外部统一调用入口,代替直接调用func void call_func() { base_func_called = false; func(); if (!base_func_called) { throw std::runtime_error("子类重写func时未调用Base::func"); } } }; class ChildExplicit : public Base { public: void func() override { Base::func(); // 调用后自动标记为已调用 std::cout << "child class explicitly calling Base::func" << std::endl; } }; class ChildImplicit : public Base { public: void func() override { // 未调用Base::func,调用call_func时会抛出异常 std::cout << "child not explicitly calling Base::func" << std::endl; } }; int main() { try { Base* explicitChild = new ChildExplicit(); explicitChild->call_func(); std::cout << std::endl; Base* implicitChild = new ChildImplicit(); implicitChild->call_func(); } catch (const std::exception& e) { std::cout << "错误:" << e.what() << std::endl; } return 0; }
如果需要多层继承每一层的实现都必须被调用,只需要每层维护自己的调用标记,在call_func中逐层校验即可。
方案2:改进版非虚拟接口(NVI)模式
适合对性能要求高、希望编译期就确保逻辑不会遗漏的场景,无运行时开销,多层继承场景下逻辑也不会混乱。
核心思路是把公共入口设为非虚函数,固定执行各层必走逻辑,子类只需要重写对应层级的扩展点:
#include <iostream> class Base { public: // 非虚公共入口,固定执行基类必走逻辑 void func() { std::cout << "Really important code was ran" << std::endl; do_base_extend(); } protected: // 基类的子类扩展点 virtual void do_base_extend() = 0; }; // 中间继承层 class MidLayer : public Base { protected: // 中间层的子类扩展点 virtual void do_mid_extend() = 0; private: // 重写基类扩展点,固定执行中间层必走逻辑 void do_base_extend() override { std::cout << "中间层必走的代码逻辑" << std::endl; do_mid_extend(); } }; // 最终实现子类 class FinalChild : public MidLayer { protected: // 只需要重写当前层级的扩展点即可 void do_mid_extend() override { std::cout << "最终子类的自定义逻辑" << std::endl; } }; int main() { Base* child = new FinalChild(); child->func(); // 输出顺序固定为:基类逻辑→中间层逻辑→子类逻辑,不会遗漏任何层级的必走代码 return 0; }
内容的提问来源于stack exchange,提问作者Gian Laager
相关产品推荐
相关产品推荐

