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

相关推荐
2401_8414956414 小时前
【数据结构】B+树
数据结构·数据库·c++·b+树·概念·结构·操作原理
小保CPP15 小时前
OCR C++ Tesseract按行识别字符
c++·人工智能·ocr·模式识别·光学字符识别
风曦Kisaki15 小时前
# Linux笔记:操作系统优化与资源管理
linux·运维·服务器·笔记
小弥儿15 小时前
GitHub今日热榜 | 2026-07-19
人工智能·学习·github·知识图谱
阿米亚波15 小时前
【C++】流式数据输入处理(不完全整理)
开发语言·c++·笔记
皓月斯语17 小时前
【C++基础】三目运算符
开发语言·数据结构·c++
zhangrelay17 小时前
笔记本轻量高品质延寿工具完整分系统清单
运维·笔记·学习
巴巴媛66617 小时前
STM32学习笔记【38.DHT11实验】
笔记·stm32·学习
初願致夕霞17 小时前
C++懒汉单例设计详解
服务器·开发语言·c++