- 条款36说明了重新定义一个继承而来的non-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*
Shape *pr = new Rectangle; //静态类型为Shape*
ps,pc,pr,都被声明为pointer-to-Shape类型,所以他们都以它为静态类型,
对象的所谓动态类型,则是指"目前所指对象的类型".也就是说,动态类型可以表现出一个对象将会发生什么行为;pc
的动态类型为Circle*
,pr
的动态类型为Rectangle *
,ps
没有动态类型
virtual函数是动态绑定,而缺省参数值却是静态绑定
,意思是你可能会在"调用一个定义于derived class
内的virtual
函数的同时,却使用base class
为它所制定缺省参数值":
pr->draw(); // 调用Rectangle::draw(Shape::Red)
为什么C++以这种方式来运作呢? 答案在于运行效率,如果缺省参数值是动态绑定,编译器就必须有某种办法在运行期为virtual函数决定适当的参数缺省值,这比目前实行的"在编译器决定"的机制更慢而且更加复杂;
但是如果你同时提供缺省参数值给base
和derived class
用户,又会发生什么事情的?
clas 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内的缺省参数值改变了,derived classes也必须改变;解决办法是令base class
内的一个public non-virtual
函数调用private virtual
函数,后者可被derived classes
重新定义
clas Shape {
public:
enum ShapeColor{ Red, Green, Blue};
void draw(ShapeColor color = Red) const {
doDraw(color);
}
private:
virtual void doDraw(ShapeColor color) const =0; // 真正的工作在此处完成
};
class Rectangle : public Shape {
public:
//...
private:
virtual void doDraw(ShapeColor color) const; //无需指定缺省参数值
};