最近开始看《Effective C++》,为了方便以后回顾,特意做了笔记。若本人对书中的知识点理解有误的话,望请指正!!!
如果在基类中有一个 public 函数 mf,创建一个派生类对象 x ,然后创建一个基类指针指向 x 调用这个函数,创建一个派生类指针指向 x 调用这个函数,两者调用的函数相同,那么结果应该是一样的吗? 一般来说会是一样的,但如果 mf 是个普通函数且派生类重新定义 mf 函数,将会遮掩基类的相应函数,通过派生类调用该函数,永远是派生类的版本,而非基类的版本。
class B {
public:
void mf();
...
};
class D : public B {
public:
void mf();
...
};
D x;
B* pB = &x;
D* pD = &x;
pB->mf();
pD->mf();
造成这样现象的原因是,普通l函数如 B::mf 和 D::mf 都是静态绑定(statically bound,见条款37)。意思是由于 pB 的类型是 B* ,通过 pB 调用的普通函数永远是 B 所定义的版本,即使 pB 指向一个类型为 B 派生出来的类对象。 但虚函数就是动态绑定(dynamically bound,见条款37),所以,它们不会出现这个问题。如果 mf 是个虚函数,不论是通过 pB 或 pD 调用 mf ,都会导致调用 D::mf ,因为 pB 和 pD 真正指向的都是一个类型为 D 的对象。
如果你设计了 D 类并重新定义从基类 B 继承来的普通函数 mf,当 D 对象调用 mf 函数都可能表现出 B 或 D 的行为。决定因素不再对象自身,而在于“指向该对象的指针”当初的声明类型。引用造成的结果和指针差不多。
在之前的条款32中说过,public 继承 意味着 is-a 关系。条款34中也描述了为什么在 class 内声明一个 普通函数 会为该 class 建立起一个不变性,凌驾其特殊性。如果这两个观点放在 B 类 和 D 类以及 普通成员函数 B::mf 身上,那么:
- 适用于 B 对象的每一件事,也适用于 D 对象,因为每个 D 对象都是一个 B 对象
- B 类的派生类一定会继承 mf 的接口和实现,因为 mf 是 B 的一个 普通函数
如果 D 重新定义 mf ,你的设计就出现矛盾了,如果 D 真有必要实现出与 B 不同的 mf ,那么 D 继承 B 干嘛?如果 D 必须继承自 B,且 D 的 mf 与 B 的 mf 不同,那么就应该将 mf 声明为虚函数,而不是普通函数。
从中可得出一个结论:任何情况下都不该重新定义一个继承而来的 普通函数。
条款37:绝不重新定义继承而来的默认参数值
|