《Effective C++》学习笔记(条款36:绝不重新定义继承而来的non-virtual函数)

最近开始看《Effective C++》,为了方便以后回顾,特意做了笔记。若本人对书中的知识点理解有误的话,望请指正!!!

如果在基类中有一个 public 函数 mf,创建一个派生类对象 x ,然后创建一个基类指针指向 x 调用这个函数,创建一个派生类指针指向 x 调用这个函数,两者调用的函数相同,那么结果应该是一样的吗?
一般来说会是一样的,但如果 mf 是个普通函数且派生类重新定义 mf 函数,将会遮掩基类的相应函数,通过派生类调用该函数,永远是派生类的版本,而非基类的版本。

class B {
public:
    void mf();
    ...
};
class D : public B  {
public:
    void mf();    // 遮掩了基类B的mf函数(可详见条款33)
    ...
};

D x;
B* pB = &x;
D* pD = &x;
pB->mf();    // 调用 B::mf
pD->mf();    // 调用 D::mf

造成这样现象的原因是,普通函数 如 B::mfD::mf 都是静态绑定(statically bound,见条款37)。意思是由于 pB 的类型是 B*,通过 pB 调用的普通函数永远是 B 所定义的版本,即使 pB 指向一个类型为 B 派生出来的类对象。
但虚函数就是动态绑定(dynamically bound,见条款37),所以,它们不会出现这个问题。如果 mf 是个虚函数,不论是通过 pBpD 调用 mf ,都会导致调用 D::mf ,因为 pBpD 真正指向的都是一个类型为 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:绝不重新定义继承而来的默认参数值

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值