侧边栏壁纸
博主头像
如此肤浅

但行好事,莫问前程!

  • 累计撰写 52 篇文章
  • 累计创建 6 个标签
  • 累计收到 6 条评论

目 录CONTENT

文章目录

条款34 区分接口继承和实现继承

如此肤浅
2022-06-07 / 0 评论 / 0 点赞 / 24 阅读 / 2,282 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-06-07,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1. 接口继承和实现继承不同。在 public 继承之下,derived classes 总是继承 base class 的接口。
2. pure virtual 函数只具体指定接口继承。
3. 简朴的(非纯)impure virtual 函数具体指定接口继承及缺省实现继承。
4. non-virtual 函数具体指定接口继承以及强制性实现继承。

public 继承的概念由两部分组成:函数接口继承和函数实现继承。身为 class 设计者,有时候你会希望 derived classes 只继承成员函数的接口(也就是声明);有时候你又会希望 derived classes 同时继承函数的接口和实现,但又希望能够重写它们所继承的实现;又有时候你希望 derived classes 同时继承函数的接口和实现,并且不允许重写任何东西。

案例

class Shape {
public:
	// 纯虚函数,画出形状
	virtual void draw() const = 0; 
    // 非纯  虚函数,供成员函数输出报错
    virtual void error(const std::string& msg); 
    // 非虚函数,返回对象的唯一标识
    int objectID() const;
    ...
};
class Rectangle: public Shape { ... };
class Ellipse: public Shape { ... };

Shape 是个抽象 class,它的 pure virtual 函数 draw 使它成为一个抽象 class。所以客户不能够创建 Shape class 的实体,只能创建其 derived classes 的实体。

  • 声明 pure virtual 函数的目的是为了让 derived classes 只继承函数接口
    Shape::draw 的声明式告诉具象 derived classes 设计者说,“你必须提供一个 draw 函数,但我不干涉你怎么实现它。”

    我们也可以为 pure virtual 函数提供定义,也就是说你可以为 Shape::draw 提供一份实现代码,调用它时需要明确指出其 class 名称。如下:

    Shap* ps = new Shape; // 错误!Shape是抽象的,不可实例化
    Shape ps1 = new Rectangle; // 没问题
    ps1->draw(); // 调用Rectangle::draw
    Shap* ps2 = new Ellipse; // 没问题
    ps2->draw(); // 调用Ellipse::draw
    ps1->Shape::draw(); // 调用Shape::draw
    ps2->Shape::draw(); // 调用Shape::draw
    
  • 声明(非纯)impure virtual 函数的目的,是让 derived classes 继承该函数的接口和缺省实现
    impure virtual 函数和 pure virtual 函数有点不同。一如往常,derived classes 继承其函数接口,但 impure virtual 函数会提供一份实现代码,derived classes 可能重写它。

    在 Shap::error 这个例子中:

    class Shape {
    public:
    	virtual void error(const std::string& msg);
        ...
    };
    

    其接口表示,每个 class 都必须支持一个“当遇上错误时可调用”的函数,但每个 class 可自由处理错误。如果某个 class 不想针对错误做出任何特殊行为,它可以退回到 shape class 提供的缺省错误处理行为。也就是说 shape::error 的声明式告诉 derived classes 的设计者,“你必须支持一个 error 函数,但如果你不想自己写一个,可以使用 shape class 提供的缺省版本”。

  • 声明 non-virtual 函数的目的是为了令 derived classes 继承函数的接口及一份强制性实现。
    如果成员函数是个 non-virtual 函数,意味是它并不打算在 derived classes 中有不同的行为。

    class Shape {
    public:
    	int objectID() const;
        ...
    };
    

    在 Shape::objectID 的声明中它想做的是“每个 Shape 对象都有一个用来产生对象唯一标识的函数,此标识码总是采用相同的计算方法,该方法由 Shape::objectID 的定义式决定,任何 derived class 都不应该尝试改变其行为”。

pure virtual 函数、simple(impure) virtual 函数、non-virtual 函数之间的差异,使你得以精确指定你想要 derived classes 继承的东西:只继承接口、或是继承接口和一份缺省的实现、或是继承接口和一份强制的实现。

  • 应该避免的两个常犯错误:
    • 第一个错误是将所有函数声明为 non-virtual。这使得 derived classes 没有余裕空间进行特化工作。non-virtual 析构函数尤其会带来问题(见条款7)。当然啦,设计一个并不想成为 base class 的 class 是绝对合理的,既然这样,将其所有成员函数都声明为 non-virtual 也很适当。但这种声明如果不是忽略了 virtual 和 non-virtual 函数之间的差异,就是过度担心 virtual 函数的效率成本。实际上任何 class 如果打算被用来当做一个 base class,都会拥有若干 virtual 函数。
    • 另一个常见错误是将所有成员函数声明为 virtual。有时候这样做是正确的,例如条款31的 Interface classes。然而某些函数就是不该在 derived class 中被重新定义,果真如此你应该将那些函数声明为 non-virtual。
0

评论区