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