美文网首页C++ 杂记
条款 36:绝不重新定义继承而来的 non-virtual 函数

条款 36:绝不重新定义继承而来的 non-virtual 函数

作者: 赵者也 | 来源:发表于2017-08-02 11:28 被阅读6次

    Effective C++ 中文版 第三版》读书笔记

    条款 36:绝不重新定义继承而来的 non-virtual 函数

    class B { 
    public: 
        void mf(); 
        ... 
    }; 
    
    class D : public B {...};
    
    D x;
    

    如果以下行为:

    B* pB = &x;
    pB->mf();
    

    异于以下行为:

    D* pD = &x;
    pD->mf();
    

    你可能相当惊讶。两者的行为确实应该相同,但是如果 mf 是个 non-virtual 函数而 D 定义有自己的 mf 版本:

    class D : public B { 
    public: 
        void mf(); 
        ... 
    }; 
    
    pB->mf();//调用B::mf 
    pD->mf();//调用D::mf
    

    造成这一行为的原因是,non-virtual 函数都是静态绑定的。由于 pB 被声明为一个 pointer-to-B,通过 pB 调用的 non-virtual 函数永远是 B 所定义的版本,即使 pB 指向一个类型为 “B 派生之 Class” 的对象。

    virtual 函数是动态绑定的,如果 mf 是个 virtual 函数,不论通过 pB 还是通过 pD 调用 mf,都会导致调用 D::mf,因为 pB 和 pD 真正指的都是 D 的对象。

    reference 也会展现和指针一样难以理解的这中精神分裂的不一致行为。

    public 继承意味着 is-a 关系。

    如果有 non-virtual 函数 mf,B 的 derived class 一定会继承 mf 的接口和实现。

    现在,如果 D 重新定义 mf,你的设计就出现矛盾。如果 D 真有必要实现出与 B 不同的 mf,并且如果每一个 B 对象 —— 不管多么特化 —— 真的必须使用 B 所提供的 mf 实现码,那么 “每个 D 都是一个 B” 就不为真。既然如此,D 就不应该以 public 形式继承 B。

    另一方面,如果 D 真的必须以 public 方式继承 B,并且 D 真有需要实现出与 B 不同的 mf,那么 mf 就无法为 B 反应出 “不变性凌驾特异性” 的性质。既然这样 mf 应该声明为 virtual 函数。

    最后,如果 D 真的是个 B,并且如果 mf 真的为 B 反应出 “不变性凌驾特异性” 的性质,那么 D 便不需要重新定义 mf,而且也不应该尝试这样做。

    请记住:
    绝对不要重新定义继承而来的 non-virtual 函数

    相关文章

      网友评论

        本文标题:条款 36:绝不重新定义继承而来的 non-virtual 函数

        本文链接:https://www.haomeiwen.com/subject/ysadlxtx.html