条款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函数。