前言
假设我们有base class与derived class如下:1
2
3
4
5class B{
public:
void mf();
}
class D:public B {...}
我们应该确定:1
2
3D x;
B* pB = &x;pB->mf();
D* pD = &x;pD->mf();
两次函数调用的行为应当一致。
问题实例
仍以前文为例,如果我们在D中重定义了mf,也就是说造成了遮蔽名称的现象,那会有如下情况发生1
2
3
4
5D x;
B* pb= &x
D * pd = &x;
pb->mf();//调用B::mf();
pd->mf();//调用D::mf();
造成如上行为的原因是因为non-virtual静态绑定,具体来说,如果pb是一个pointer to B,通过pb调用的non-virtual函数永远是B的版本。
但如果mf是一个动态绑定的virtual,那么无论通过pb还是pd调用得到的都是D::mf().
non-virtual被重定义的危害
上述实例表明,在发生了non-virtual重定义的情况下,D对象的行为出现了不一致性。具体来说,当mf被调用时,对象的行为可能表现为基类也可能表现为派生类,其决定因素不在于对象自身而在于“指向该对象的指针”的声明类型。
前文已述,public继承代表is-a,而non-virtual代表了class的某种不变性。如果derived class重定义了non-virtual函数,设计便出现了矛盾:如果D真的有必要实现出与B不同的non-virtual函数,也就表明其特异性超过了不变性,这也就违背了is-a关系。
base class的析构函数之所以要声明为virtual也是如此,因为derived class绝不应该重新定义一个继承而来的non-virtual函数。