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

《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 函数)