条款37:绝不重新定义继承而来的缺省参数值

本文探讨了在C++中为何不应重新定义继承而来的虚函数的缺省参数值,并提出了使用非虚接口(NVI)手法来解决该问题的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

条款37:绝不重新定义继承而来的缺省参数值

内容:
     审视了一下条款以后,我们可以换一种说法:绝不重新定义继承而来的virtual函数或non-virtual函数的缺省参数值.而在上一款中我们提到,绝对不要试图去重新定义继承而来的non-virtual函数,将这句话与本条款合并的话,我们就可以将本条款的内容简化为:绝不重新定义继承而来的virtual函数的缺省参数值.为什么继承而来的virtual函数的缺省参数值不能被重新定义呢?其实原因也挺简单:缺省参数是静态绑定,而virtu-

al函数是动态绑定. 至于什么是动态绑定与静态绑定,我想大家都应该清楚,不过在这里我还是要简单的唠叨一下,加强一下理解也不是件坏事,呵呵!所谓对象的静态绑定也叫前期绑定,它是说该对象类型和行为在程序编译期间就可以确定,例如:
     class Shape{
     public:
              enum Color{RED,GREEN,BLUE};
              virtual void draw(Color color = RED)const = 0;
              ...
     };
     class Circle:public Shape{
     public:
              //哦欧! 竟然改变缺省参数值
              virtual void draw(Color color = GREEN)const{ ... }
     };
     class Rectangle:public Shape{
     public:
             //没用指定参数类型,需要用户去明确的指定其值
            //静态绑定下不继承基类的缺省值,若以指针或引用调用则不需要指定缺省值,因为动态绑定
            //继承基类的参数缺省值
            virtual void draw(Color color)const{ ... }
     };
     看一下下面几个指针:
     Shape* ps;
     Shape* pc = new Circle;
     Shape* pr = new Rectangle;
     这里的ps,pc,pr不管它具体指向的是什么对象,他们的静态类型都是Shape*.而动态类型就是它们真正指向的对象的类型.故pc的动态类型为Circle*,而pr的动态类型为Rectangle*,ps由于没用指向任何对象,所以此时没有动态类型. 好,关于这两种类型的特点及差异我们就讨论到这里,现在我们来看下面这条语句:
     pc->draw(); //注意调用的是: Circle::draw(RED)
     哦欧,偶滴个神呐!怎么会调用Circle::draw(RED),不伦不类的?为什么不是Circle::draw(GREEN)?别急,仔细分析一下,我们就会发现去解释这种"怪事"的出现原因也并不那么复杂:首先根据其调用语句用指针这一事实,我们就知道了其调用的版本应该是该指针的动态类型的函数版本,即Circle::draw,这个问题不大.下面我们来看它的传值参数,前面我们提到缺省参数值是静态绑定的,而pc的静态类型是Shape*,所以该参数的传入值是Shape的该函数版本的缺省值.明白了吧!呵呵!
     晕死,原来是这样,那这时候你是不是禁不住问:为什么C++坚持以这种乖张的方式来运作呢?答案在于运行期效率,如果缺省值也是动态绑定的,那么编译期就必须要有办法在运行期为virtual函数决定适当的参数缺省值.如果这样做的话,就要比目前实现的"在编译期决定"的机制更慢而且更复杂,考虑到执行速度和实现上的简易性,C++放弃了这样的做法.
     好,现在,你为了遵循本款约定却同时提供缺省参数值给你的基类和父类,代码就这样了:
     class Shape{
     public:
              enum Color{RED,GREEN,BLUE};
              virtual void draw(Color color = RED)const = 0;
              ...
     };
     class Circle:public Shape{
     public:
              virtual void draw(Color color = RED)const {...}
     };
     明显的是代码重复嘛!何况你要是想改变缺省值的话,必须要同时改变基类和子类函数的缺省值,一不小心,就会出现漏改或写错的情况,导致意想不到的错误出现.有没用一种更方便的写法呢?当然,你还记得NVI手法吗?额..,(non-virtual interface),要是忘记的话,回过头看看条款35,用这种手法的话,我们写下代码如下:
     class Shape{
     public:
              enum Color{RED,GREEN,BLUE};
              void draw(Color color = RED) const{
                     ...
                     doDraw(color);
                     ...
              }
              ...
     private:
             virtual void doDraw(Color color) const = 0;  
     };
     class Circle:public Shape{
                   ...
     private:
              virtual void doDraw(Color color){ ... }
     };
     由于draw是non-virtual而non-virtual绝对不会被重新改写(条款36),所以color的缺省值总是为RED.
     请记住:
     ■ 绝对不要重新定义一个继承而来的缺省参数值,因为缺省参数值都是静态绑定,而virtual函数-你唯一应该覆写的东西-却是动态绑定.

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值