《Effective C++》学习笔记——条款37

来源:互联网 发布:按键精灵 查询数据库 编辑:程序博客网 时间:2024/06/05 21:11


六、继承与面向对象设计


条款37、绝不重新定义继承而来的缺省参数值




  • 本条款主要讨论:继承一个带有缺省参数的virtual函数
  • 本条款成立的依据: 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 = Green) const;    ...};class Circle : public Shape  {public:    virtual void draw(ShapeColor color) const;    // 注释①    ...};Shape *ps;Shape *pc = new Circle;Shape *pr = new Rectangle;

现在,Rectangle类 与 Circle类 都继承自 Shape类。
ps、pc、pr 在声明时,是指向Shape的指针,所以,静态类型都是 Shape*
但 pc、pr 指向了两个类,所以它们动态类型分别为 Circle* 与 Rectangle* (ps未指向任何对象,所以没有动态类型)

pc->draw(ShapeColor::Red);pr->draw(ShapeColor::Red);

分别相当于调用:

Circle::draw(ShapeColor::Red)Rectangle::draw(ShapeColor::Red)

但,如果是

pr->draw();

理论上来讲,pr的动态类型是Rectangle* ,所以应该是 Rectangle::draw(ShapeColor::Green), 但由于 pr 的静态类型是Shape*, 它缺省参数值来自Shape class,也就是 ShapeColor::Red。

原因 与 错误的方法

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;    ...};

—— 代码重复,而且不易修改维护


替换它, NVI手法

当然,对于这种需求,也非束手无策。
既然这条路行不通,那我们换条路,用别的替代它。
在条款35中就有许多替代方法,这里讲述 NVI手法
让基类的public non-virtual函数 调用 private virtual函数,让 non-virtual函数负责指定缺省参数值,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 {public:    ...private:    virtual void doDraw(ShapeColor color) const;    ...};

因为 non-virtual函数 绝对不应该被重定义(根据条款36),所以draw函数的 color缺省值,应该永远为ShapeColor::Red

请记住
  • 绝对不要重新定义一个继承而来的缺省参数值,因为缺省参数值都是静态绑定,而virtual函数(唯一应该被重定义的东西)是动态绑定。





注释:

  • ①. 这样定义以后,如果用户通过对象调用此函数,一定要指定参数值。因为,静态绑定下这个函数并不会继承基类的缺省参数值。但如果通过指针(或引用)来调用这个函数,就可以不指定参数值,因为动态绑定下这个函数会继承基类的缺省参数值。




0 0
原创粉丝点击