EffectiveC++读书笔记——item37(不要重定义通过继承得来的缺省函数值)

发布于:2025-02-13 ⋅ 阅读:(98) ⋅ 点赞:(0)
  1. 静态绑定与动态绑定的差异:虚拟函数是动态绑定,即调用的特定函数取决于对象的动态类型;而缺省参数值是静态绑定,取决于对象的静态类型。例如:
// a class for geometric shapes
class Shape {
public:
    enum ShapeColor { Red, Green, Blue };

    // all shapes must offer a function to draw themselves
    virtual void draw(ShapeColor color = Red) const = 0;
};

class Rectangle : public Shape {
public:
    // notice the different default parameter value — bad!
    virtual void draw(ShapeColor color = Green) const;
};

class Circle : public Shape {
public:
    virtual void draw(ShapeColor color) const;
};

Shape *ps;                       // static type = Shape*
Shape *pc = new Circle;          // static type = Shape*
Shape *pr = new Rectangle;       // static type = Shape*
pc->draw(Shape::Red);             // calls Circle::draw(Shape::Red)
pr->draw(Shape::Red);             // calls Rectangle::draw(Shape::Red)
pr->draw(); // calls Rectangle::draw(Shape::Red)! 调用Rectangle的draw函数,但使用了Shape的缺省参数值Red

在上述代码中,pr 的动态类型是 Rectangle*,静态类型是 Shape*,当调用 pr->draw() 时,虽然调用的是 Rectangledraw 函数,但由于缺省参数值是静态绑定,使用的是 Shape 类中的缺省参数值 Red,而不是 Rectangle 类中重定义的 Green,这可能导致意外结果。

  1. C++ 如此设计的原因:为了运行时效率,若缺省参数值动态绑定,编译器需在运行时确定其值,这比在编译期确定更慢且复杂。

  2. 避免代码重复的方法:若在基类和派生类中提供相同的缺省参数值会导致代码重复,可考虑使用非虚拟接口惯用法(NVI idiom)。例如:

class Shape {
public:
    enum ShapeColor { Red, Green, Blue };

    void draw(ShapeColor color = Red) const           // now non - virtual
    {
        doDraw(color);                                  // calls a virtual
    }

private:
    virtual void doDraw(ShapeColor color) const = 0;  // the actual work is
};

class Rectangle : public Shape {
private:
    virtual void doDraw(ShapeColor color) const;
};

在这种设计中,通过基类中的公有非虚拟函数 draw 指定缺省参数,调用派生类可能重定义的私有虚拟函数 doDraw 做实际工作,明确了 drawcolor 参数的缺省值永远是 Red

总之,由于缺省参数值的静态绑定和虚拟函数的动态绑定特性,在继承体系中重定义继承的缺省参数值会导致不符合预期的行为,应避免这种做法,可采用合适的替代设计如 NVI idiom。


网站公告

今日签到

点亮在社区的每一天
去签到