- Rule37 :绝不重新定义继承而来的缺省参数值
Never redefine a function’s inherited default parameter value.
由于重新定义一个继承而来的Non-virtual函数永远是错误的,所以可以安全地将本条款的讨论局限于”继承一个带有缺省参数值的Virtual函数”。
#include "stdafx.h"
#include <iostream>
using namespace std;
class Shape{
public:
enum ShapeColor{Red,Green,Blue};
virtual void draw(ShapeColor color = Blue) const = 0;//blue = 2
};
class Rectangle : public Shape
{
public:
virtual void draw(ShapeColor color = Green) const{
//赋予不同的缺省参数值,这容易出现错误
cout<<"draw color is "<<color<<endl;
}
};
class Circle : public Shape
{
public:
virtual void draw(ShapeColor color) const{
cout<<"draw color is "<<color<<endl;
}
//以上这么写则当客户已对象调用此函数,一定要指定参数值。因为静态绑定下这个函数并不从其base继承缺省参数值。
//若以指针或引用调用此函数,可以不指定参数值,因为动态绑定下这个函数会从其base继承缺省参数值
};
int _tmain(int argc, _TCHAR* argv[])
{
Rectangle rec;
rec.draw(); // Green
Shape &ss = rec;
ss.draw();//传递的是基类的blue,所以这种情况下,就会让调用产生误解
Circle cir;
cir.draw(Shape::Blue);这种方式必须为其提供参数值
Shape& s = cir;
s.draw();//这种动态绑定,默认参数可以传递给继承类进行调用。 显示的值为Blue 2
getchar();
return 0;
}
究其原因是当考虑带有缺省参数值的Virtual函数时,Virtual函数时动态绑定,而缺省参数值却是静态绑定。就是说可能会在“调用一个定义于Derived class内的Virtual函数”的同时,却使用base class为它所指定的缺省参数值。就像上面所示的例子。
以上事实在将引用换成指针的情况下,仍然存在。重点在于draw是个virtual函数,而它有个缺省参数值在derived class中被重新定义。
为什么C++坚持这种方式来运行呢?答案在于运行期效率。如果缺省参数值是动态绑定,编译器就必须有某种办法在运行期为Virtual函数决定适当的参数缺省值,这比目前实行的“在编译期决定”的机制更慢而且更复杂。为了执行速度和编译器实现上的简易,C++做了这样的取舍。
解决方案:
当你想令Virtual函数表现出你所想要的行为遭到问题时,聪明的做法是考虑替代设计。替代设计之一是NVI(non-virtual interface)手法:令base class内的一个public non-virtual 函数调用private Virtual函数,后者可被Derived classes重新定义。这里可以让non-virtual函数指定缺省参数,而private Virtual函数负责真正的工作。
class 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
{
private:
virtual void doDraw(ShapeColor color) const
{
cout<<"this is the "<<color<<endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
////问题所在地
//Rectangle rec;
////rec.draw();
//Shape &ss = rec;
//ss.draw();
//Circle cir;
////cir.draw(Shape::Blue);
//Shape& s = cir;
//s.draw();
//使用NVI(non-virtual interface)方法解决
Rectangle rec;
rec.draw(Shape::Blue);
Shape &ss = rec;
s.draw(); //现在这两种调用一致了
getchar();
return 0;
}