news 2026/9/16 18:46:49

你真的了解C++吗》No.030:非虚函数在继承体系中的设计原则——NVI 模式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
你真的了解C++吗》No.030:非虚函数在继承体系中的设计原则——NVI 模式

《你真的了解C++吗》No.030:非虚函数在继承体系中的设计原则——NVI 模式

导言:谁才是流程的控制者?

在传统的面向对象设计中,我们习惯于直接调用虚函数。但在复杂的工业级代码中,这种做法会产生一个弊端:基类无法在虚函数执行前后进行“统一管理”(比如加锁、记录日志或合法性检查)。

为了解决这个问题,C++ 社区推崇一种设计模式:NVI (Non-Virtual Interface,非虚接口) 原则


一、 NVI 的核心:公有接口非虚,虚函数私有化

NVI 原则的核心思想非常简单:

  1. 外界接口:声明为public,但必须是非虚(non-virtual)的。
  2. 具体实现:声明为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函数作为入口,往往是更成熟的做法。

🟢 第三阶段·总结

至此,我们已经看透了 C++03 继承体系的每一层皮肉:从虚表的内存布局(No.021),到菱形继承的纠缠(No.024),再到现在的 NVI 设计准则(No.030)。


下一阶段预告:我们将告别运行时的多态,进入一个“在编译阶段就解决一切”的神奇领域。在这里,代码不是运行出来的,而是“算”出来的。

➡️《你真的了解C++吗》No.031:模板是“宏”的加强版吗?——模板推导的类型安全与物理真相。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 9:57:57

SAP Fiori开发学习路线指南

以下是为你整理的SAP Fiori开发入门路线与实践指南,涵盖了从基础知识到实际开发的学习路径、关键资源及核心建议。 📚 第一阶段:入门与基础 (第1-2个月) 本阶段目标是建立知识框架,了解Fiori是什么、能做什么。 学习目标核心内…

作者头像 李华
网站建设 2026/9/6 9:35:32

嵌入式AI测试:汽车ECU软件的实时性验证

一、实时性验证的核心挑战 在汽车电子领域,实时性并非单纯指执行速度,而是指系统在确定性时间阈值内完成关键任务的能力。例如自动紧急制动系统(AEB)要求刹车信号从检测到执行需在200ms内完成,涉及多ECU协同与总线传输…

作者头像 李华
网站建设 2026/9/11 18:39:25

单相交流稳压电路的Matlab仿真探索

单相交流稳压电路 仿真及其详细的文字说明工作原理参数设计仿真说明结论心得等。 。matlab2021b一、引言 在电力系统中,电压稳定对于各种电气设备的正常运行至关重要。单相交流稳压电路能够有效维持输出电压的稳定,为负载提供可靠的电能。本文将借助Matl…

作者头像 李华
网站建设 2026/9/16 15:10:43

电网“数字配电房”:局放AI模型故障定位缩到30厘米

配电房是电网供电的核心节点,承担着电能分配、电压转换的关键职能,局部放电(简称“局放”)是配电房设备绝缘老化、故障萌发的核心前兆,若不能精准定位故障点,易引发设备损坏、供电中断等事故。传统配电房局…

作者头像 李华
网站建设 2026/9/15 11:57:09

厄达替尼Erdafitinib基于血磷水平的剂量调整方案

厄达替尼(Erdafitinib)作为全球首个获批用于治疗FGFR基因突变阳性局部晚期或转移性尿路上皮癌的口服靶向药物,其剂量调整策略的核心在于通过动态监测血磷水平实现个体化给药。这一策略基于药物作用机制与临床实践数据的深度结合,既…

作者头像 李华
网站建设 2026/9/14 1:57:58

win11安装mysql

下载: https://dev.mysql.com/downloads/mysql/8.0.html 安装mysql数据库服务端 下载完成后,得到“mysql-8.0.45-winx64.msi”安装包,开始安装 选择典型安装 点击install 安装完成后,发现没有mysql服务 且打不开“MySQL 8.0 Com…

作者头像 李华