2026/4/18 8:57:22
网站建设
项目流程
网站空间的存放种类,wordpress网页设定,wordpress的新建页面功能,什么是网络营销环境《你真的了解C吗》No.030#xff1a;非虚函数在继承体系中的设计原则——NVI 模式
导言#xff1a;谁才是流程的控制者#xff1f;
在传统的面向对象设计中#xff0c;我们习惯于直接调用虚函数。但在复杂的工业级代码中#xff0c;这种做法会产生一个弊端#xff1a;基…《你真的了解C吗》No.030非虚函数在继承体系中的设计原则——NVI 模式导言谁才是流程的控制者在传统的面向对象设计中我们习惯于直接调用虚函数。但在复杂的工业级代码中这种做法会产生一个弊端基类无法在虚函数执行前后进行“统一管理”比如加锁、记录日志或合法性检查。为了解决这个问题C 社区推崇一种设计模式NVI (Non-Virtual Interface非虚接口) 原则。一、 NVI 的核心公有接口非虚虚函数私有化NVI 原则的核心思想非常简单外界接口声明为public但必须是非虚non-virtual的。具体实现声明为virtual但通常建议设为private或者protected如果子类需要显式调用。classGameCharacter{public:// 外界调用的唯一接口非虚voidheal(intamount){// [前置工作]记录日志检查角色是否已经死亡if(isDead())return;doHeal(amount);// 调用真正的业务逻辑// [后置工作]更新 UI触发数值溢出检查clampHealth();}private:// 真正的逻辑虚函数私有virtualvoiddoHeal(intamount)0;};classWarrior:publicGameCharacter{private:virtualvoiddoHeal(intamount){// 战士特有的回血逻辑}};二、 为什么这样做NVI 的三大优势1. 强制性的“切面”逻辑通过非虚的heal()基类确保了无论哪个子类在执行回血逻辑之前都必须先经过“死亡检查”。子类只需要关注“怎么回血”而不需要关注“什么时候能回血”。2. 接口与实现的分离public接口代表了稳定性而private virtual代表了灵活性。你可以随意更改虚函数的签名或逻辑只要非虚的包装函数不变外界调用者甚至不需要感知。3. 解决“虚函数可见性”的悖论很多人奇怪基类私有的虚函数子类能重写吗答案是能虚函数的重写Override取决于函数名和签名而不是访问权限。派生类虽然不能“调用”基类的私有虚函数但完全可以“提供”一个新的实现放入虚表vtbl中。三、 特例什么时候该用protected virtual在 NVI 模式下如果派生类在实现自己的虚函数时需要显式地调用基类的实现即Base::doSomething()那么这个虚函数必须设为protected。否则请坚持使用private以实现最强的封装。总结架构师的权力NVI 原则是对“模板方法Template Method”设计模式的完美实践。它让基类成为了流程的独裁者而让子类成为了任务的执行者。在设计复杂的类层次结构时先写一个非虚的public函数作为入口往往是更成熟的做法。 第三阶段·总结至此我们已经看透了 C03 继承体系的每一层皮肉从虚表的内存布局No.021到菱形继承的纠缠No.024再到现在的 NVI 设计准则No.030。下一阶段预告我们将告别运行时的多态进入一个“在编译阶段就解决一切”的神奇领域。在这里代码不是运行出来的而是“算”出来的。➡️《你真的了解C吗》No.031模板是“宏”的加强版吗——模板推导的类型安全与物理真相。