条款37:绝不重新定义继承而来的缺省参数值
本条款成立的理由非常明确:virutal函数系动态绑定,而缺省参数值却是静态绑定。
对象的静态类型,就是他在程序被声明时所采用的类型。考虑以下的class继承体系:
class Shape{ public: enum ShapeColor{Red, Green, Blue}; virtual void draw(ShapeColor color = Red) const = 0; //... }; class Recgangle: public Shape{ public: virutal void draw(ShapeColor color = Green) const; //... }; class Circle: public Shape{ public: virutal 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类型,所以它们都为静态类型。不论他们真正指向什么,他们的静态类型都是shape*。
对象的所谓动态类型则是指目前所值对象的类型,pc的动态类型是Circle*, pr的动态类型是Rectangle*。ps没有动态类型,因为它尚未指向任何对象。动态类型可以在执行过程中改变:
ps = pc; //ps的动态类型如今是Circle*
ps = pr; //ps的动态类型如今是Recrangle*
virtual函数系动态绑定而来,意思是调用一个virutal函数时,究竟调用哪一份函数实现,取决于发出调用的那个对象的动态类型:
pc->draw(Shape::Red); //调用Circle::draw
pr->draw(Shape::Red); //调用Rectangle::draw
当时考虑带有缺省参数值的virtual函数时:virtual函数是动态绑定,而缺省参数却是静态绑定,即可能会在调用一个定义于derived类内的virtual函数时,却使用base类为它指定的缺省参数值:
pr->draw(); //调用Rectangle::draw(Shape::Red)!! pr的动态类型的默认参数是Green,但是这里却使用的是静态绑定的基类的默认参数Red。
为什么C++坚持以这种方式运作呢?答案在于运行期效率。如果缺省参数是动态绑定,编译器就必须有某种办法在运行期为virtual函数决定适当的参数缺省值。这比目前实行的在编译期决定的机制更慢而且更复杂。为了程序的执行速度和编译器实现上的简易度,C++做了这样的取舍。
如果让基类的默认参数和derived类的默认参数相同就可以了,但是很明显,这会带来代码上的重复,而且会带来相依性,如果一个改变,其他的都要改变。所以这里可以考虑替代设计,见条款35中诸多virtual函数的替代设计。
请记住:
绝对不要重新定义一个继承而来的缺省参数值,因为缺省参数值都是静态绑定,而virtual函数却是动态绑定。
条款38:通过复合塑模出has-a或者"根据某物实现出"(没怎么看懂)
条款39:明智而审慎的使用private继承
条款40:明智而审慎的使用多重继承