Effective C++ 学习笔记 条款37 绝不重新定义继承而来的缺省参数值

我们先把讨论简化一下。你只能继承两种函数:虚函数和非虚函数。然而,重新定义继承来的非虚函数永远是错误的(参见条款36),所以我们可以放心地把这里的讨论限定在这样一个情境:你继承的是一个带有默认参数值的虚函数。

既然如此,本条款的理由就变得十分直截了当了:虚函数是动态绑定的,但默认参数值是静态绑定的。

什么?你说静态绑定与动态绑定的区别已经从你疲惫不堪的脑海中溜走了(说明一下:静态绑定也叫早期绑定,动态绑定也叫晚期绑定)?那我们来复习一下。

对象的静态类型是你在程序文本中声明它所具有的类型。考虑下面这个类继承体系:

继承体系图示如下:

现在考虑这些指针:

在这个例子中,ps、pc 和 pr 都被声明为指向 Shape 的指针类型,所以它们都以此为静态类型。请注意,它们实际指向什么对象完全无关紧要------静态类型始终是 Shape*。

对象的动态类型由它当前实际指向的对象的类型决定。也就是说,动态类型表明了它会表现出怎样的行为。在上面的例子中,pc 的动态类型是 Circle*,pr 的动态类型是 Rectangle*。至于 ps,它目前还没有动态类型,因为它尚未指向任何对象。

动态类型,顾名思义,可以在程序运行过程中发生变化,通常通过赋值操作来实现:

虚函数是动态绑定的,这意味着被调用的具体函数由调用时对象的动态类型决定:

这当然都是老生常谈,我知道------你肯定理解虚函数的原理。转折点在于,当你考虑带有默认参数值的虚函数时,情况就不一样了,因为正如我前面所说:虚函数是动态绑定的,但默认参数是静态绑定的。这意味着,你最终调用的可能是派生类中定义的虚函数,但使用的却是基类中的默认参数值:

在这个例子中,pr 的动态类型是 Rectangle*,所以会调用 Rectangle 的虚函数,正如你所预期的那样。然而在 Rectangle::draw 中,默认参数值是 Green。但由于 pr 的静态类型是 Shape*,这次函数调用的默认参数值取自 Shape 类,而不是 Rectangle 类!结果就是一次由 Shape 和 Rectangle 两个类中 draw 的声明拼凑而成的调用,奇怪且几乎肯定与预期不符。

在这个问题上,ps、pc 和 pr 是指针这一事实并不重要。即使它们是引用,问题依然存在。唯一关键的是:draw 是一个虚函数,且它的默认参数值在派生类中被重新定义了。

为什么 C++ 坚持要以这种反常的方式行事?答案与运行时效率有关。如果默认参数值是动态绑定的,编译器就必须在运行时确定虚函数参数的适当默认值,这比当前在编译期确定它们的机制更慢、更复杂。C++ 选择了偏向速度和实现简单的方向,结果就是你现在的执行行为是高效的,但如果你不遵守本条款的建议,它就会让人困惑。

这倒是都说得通,但如果你试图在遵守本规则的同时,又为基类和派生类的用户都提供默认参数值,会发生什么:

哎呀,代码重复了。更糟的是,这种重复还带有依赖关系:如果 Shape 中的默认参数值被修改,所有重复它的派生类也必须跟着改,否则它们就会重新定义一个继承来的默认参数值。该怎么办呢?

当你在让虚函数按你期望的方式工作这件事上遇到困难时,明智的做法是考虑替代设计方案,而条款35中充满了虚函数的替代方案。其中一种就是非虚接口(NVI)惯用法:在基类中用一个公有非虚函数去调用一个私有虚函数,后者由派生类去重新定义。在这里,我们让非虚函数指定默认参数,而让虚函数去干实际的工作:

由于非虚函数永远不该被派生类重写(参见条款36),这种设计清楚地表明:draw 的 color 参数的默认值应当始终是 Red。

切记:

1.绝不重新定义继承来的默认参数值,因为默认参数值是静态绑定的,而虚函数------你唯一应该重写的函数------却是动态绑定的。

相关推荐
PNP Robotics8 分钟前
【PNP具身解读】GPT6 Astra:具身智能新范式,大模型 + Franka机器人快速落地验证一、GPT6 Astra 背后的布局、数据与具身方向
人工智能·学习·机器学习·机器人
小李不想当小白26 分钟前
PCB结构与组成(PCB设计入门学习笔记)
经验分享·笔记·单片机·嵌入式硬件·学习·pcb工艺·pcb
那年窗外下的雪.27 分钟前
第 02 天:Linux 权限、inode、目录项与链接
学习·http
码匠许师傅38 分钟前
【C++三方组件】cxxopts:轻量首选的命令行解析
c++
XGStudio20241 小时前
免费安全的 - 二维码转换工具
学习·ai
hansang_IR1 小时前
【讲解】CSP-S2026 第一轮初赛
c++·算法
hanlin031 小时前
刷题笔记:力扣第76题-最小覆盖子串
笔记·算法·leetcode
hengmeida1 小时前
楼宇控制柜标准化项目交付完整规范
大数据·网络·笔记
Methy1 小时前
IoTHub半个月崩了五次?都是裸rethrow惹的祸
服务器·c++·后端
邵奈一1 小时前
【紧急处理ZCode事件】ZCode 工作区快照静默上传:检测方法与三层防护实践
人工智能·学习·大模型·国产