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

1、为什么要绝不重新定义继承而来的non-virtual函数?

考虑下述情况:

//情况一
class B{
public:
    void mf();
    ...
};
class D:public B{...};

D x;
B *pB = &x;
pB -> mf();//经由该指针调用	B::mf
D *pD = &x;
pD -> mf();//经由该指针调用B::mf

//情况二
class B{
public:
    void mf();
    ...
};

class D: public B{
public:
    void mf();     //遮掩了B:mf;
    ...
}
D x;
B *pB = &x;
pB -> mf();//调用B::mf
D *pD = &x;
pD -> mf();//调用D::mf        

情况一是符合我们预想的,因为派生类没有重新定义non-virtual函数。
但是情况二却是我们预想之外的,因为两个不同类型的指针指向的是同一个对象,但是发起函数调用时却调用了不同版本的函数。

2、导致上述问题的原因?

non-virtual函数都是静态绑定的,即:假如P是基类指针,无论P指向的基类对象还是派生类对象,它发起函数调用的版本都是基类的版本。

与之对应的便是virtual函数,virtual函数是动态绑定的,假如P是基类指针,如果P指向的是基类对象,由P发起的函数调用则是基类版本的,如果P指向派生类,那么由P发起的函数调用则是派生类版本的。

3、解释该条款的为什么要这么做?

(1)pubilic继承与non-virtual函数

首先public继承描述的是is-a关系
其次,non-virtual函数强调不变性,忽略特异性。

(2)基于上述两点:

那么就应该:

  • D一定是B。
  • 适用于B的行为一定适用于D。

假如重新定义了了B的函数mf(),基于编译器的上述规则,设计就出现了矛盾。

  • 如果要体现不变性:mf不应该被特化。
  • 如果要体现变化性:mf应该被设计为virtual。

因此,绝不重新定义继承而来的non-virtual函数。

posted @ 2019-12-23 11:49  江南又一春  阅读(247)  评论(0编辑  收藏  举报