条款37:绝不重新定义继承而来的缺省参数值
1、绝不重新定义继承而来的缺省参数值所牵涉的范围?
继承的函数分为两种:non-virtual
和virtual
。而non-virtual
函数是完全不能重新定义的(函数体、缺省参数都不可以),因此我们所说的绝不重新定义继承而来的缺省参数值实际指的是virtual
函数。
2、为什么不能重新定义继承而来的缺省参数值?
因为virtual
函数体是动态绑定的,但是virtual
函数的缺省参数是静态绑定的。假如通过指针或者引用访问重新定义继承而来的virtual
函数的缺省参数值,则会造成错误。(例如:函数体为派生类版本,但是缺省参数却是基类版本。)
注意:C++
编译器将 virtual
函数体设为动态的,而将virtual
的缺省参数设为静态的是出于效率的考虑。如果缺省参数设为动态的,程序运行机制会变得更慢、更复杂。
3、何谓静态绑定和动态绑定?
-
静态绑定,又叫前期绑定。
对象的静态类型,就是它在程序中被声明时所采用的类型。 -
动态绑定又叫后期绑定。
-
对象的动态类型指的是指针(或引用)目前所指对象的类型。动态类型可以表现出一个对象将会有什么行为。动态类型可以在执行过程中被改变。
举例:
//ps、pc、pr 的静态类型为 Shape*
Shape* ps; //ps 动态类型:无
Shape* pc = new Circle; //pc动态类型:圆形
Shape* pr = new Rectangle; //pr动态类型: 矩形
ps = pc;
ps = pr; //动态类型可以在运行期间改变。
4、在遵守该规则的前提下,如何做到同时给base class
和derived 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 = Red) const;
...
};
代码的缺点:
- 重复度大。(每个派生类都要指定缺省参数
ShapeColor color
等于Red
) - 灵活性差。(当基类的缺省参数改变时,所有派生类的缺省参数也要跟着修改。)
方案二:NVI手法替代方案
class Shape {
public:
enum ShapeColor {Red, Green, Blue};
void draw(ShapeColor color = Red) const //如今它是non-virtual
{
doDraw(color); //调用一个virtual
}
...
private:
virtual void doDraw(ShapeColor color) const = 0; //真正的工作在此处完成
};
class Rectangle: public Shape{
public:
...
private:
virtual void doDraw(ShapeColor color) const; //注意,无须指定缺省参数值。
...
};
Shape * rec = new Rectangle();
rec->draw(); // 缺省参数调用,缺省参数为Red 矩形版本draw()
rec->draw(Green); // 带参数调用 矩形版本draw()
优点:
- 基类缺省发生改变,派生类不需要修改参数。