条款 37 :绝不重新定义继承而来的缺省参数值
Never redefine a function’s inherited default parameter value
我们在条款36刚刚说过继承non-virtual函数是错误的。所以本条款更确切的说是:绝不重新定义继承而来的带有缺省参数值的virtual函数。理由很明确:virtual是动态绑定,而缺省参数是静态绑定
动态绑定又叫后期绑定,静态绑定又叫前期绑定。我们来复习一下两者区别吧:
我们先说一下静态类型和动态类型的概念:对象的所谓静态类型就是它在程序中被声明时所采用的类型 ,对象的动态类型指的是目前所指对象的类型,也就是说动态类型可以表现出一个对象将会有什么行为
通过代码说事儿吧:
//一个描述几何形状的class
class Shape{
public:
enum ShapeColor{Red,Green,Blue};
//所有函数都必须提供一个函数用来绘制自己
virtual void draw(ShapeColor color=Red)const=0;
...
};
class Rectangle:public Shape{
public:
//注意这里对缺省参数的改变,糟糕透了
virtual void draw(ShapeColor color=Green)const;
...
};
class Circle:public Shape{
public:
virtual void draw(ShapeColor color)const;
//用户以对象调用时必须指定参数
//因为静态绑定下,这个函数并不从base继承缺省参数值
//用户以引用调用时可以不指定参数
//因为动态绑定下,这个函数会从base继承缺省参数值
}
Shape* ps; //静态类型为Shape*,没有动态类型
Shape* pc=new Circle; //静态类型为Shape*,动态类型为Circle*
Shape* pr=new Rectangle;//静态类型为Shape*,动态类型为Rectangle*
//注意上述三个指针,不论他们真正指向什么,他们的静态类型都不会变
//动态类型则可以在程序执行过程中改变(通常是赋值引起)
//virtual函数是动态绑定,也就是说调用一个virtual函数时,
//究竟调用哪一份函数实现代码,取决于发出调用的那个对象的动态类型.
pc->draw(Shape::Red);//调用Circle::draw
pr->draw(Shape::Red);//调用Rectangle::draw
//我们来看下面的问题
pr->draw();//调用Rectangle::draw
//这里pr的动态类型是Rectangle,所以调用Rectangle::draw,
//而函数的缺省值应该是Green
//但是由于pr的静态类型是Shape*,所以缺省参数值来自Shape
//结果是你的代码出现二义性了
//为什么C++以这种奇怪的方式来运作呢?
//因为效率,如果缺省函数是动态绑定,那么编译器就必须提供某种让运行期virtual函数知道缺省值的方法
//这比在编译器决定(静态绑定)的机制更慢更复杂。
//这是为了效率而做出的取舍。
//或许你又会想能不能这么写,给缺省值一样的值
class Shape{
public:
enum ShapeColor{Red,Green,Blue};
//所有函数都必须提供一个函数用来绘制自己
virtual void draw(ShapeColor color=Red)const=0;
...
};
class Rectangle:public Shape{
public:
//注意这里对缺省参数的改变,糟糕透了
virtual void draw(ShapeColor color=Red)const;
...
};
//这更糟糕,代码重复了。。。
//更糟糕的是Shape给的缺省值一旦改变,所以派生类的该virtual function都要跟着变
请记住
绝不重新定义继承而来的缺省参数值,因为缺省参数是静态绑定,而virtual函数(你唯一应该覆写的东西)是动态绑定,