知识点:
- 多态时:基类(或子类)指针指向子类对象,利用该指针调用成员时关键点看子类对象
- 非多态时:基类(或子类)指针指向子类对象时,利用该指针调用成员时关键点看指针
对于基类B和子类D:
class B{
public:
void mf();
...
};
class D:public B{...};
我们知道对于non-virtual函数,子类是既继承接口又继承实现的,在这种情况下,以下行为:
D x;
B *pB = &x;
pB -> mf();//经由该指针调用mf
D *pD = &x;
pD -> mf();//经由该指针调用mf
以上两种情况调用的是同一个方法,因为基类和子类的方法是一模一样的。
但是,如果mf是个non-virtual函数,而D定义了自己的mf版本,则情况就不同了:
class D: public B{
public:
void mf(); //遮掩了B:mf;
...
pB -> mf(); //调用B::mf
pD -> mf(); //调用D::mf
此时基类指针指向基类函数,子类指针指向子类函数。因为non-virtual函数如B::mf和D::mf都是静态绑定的,pB被声明为指向B,pD被声明为指向D,通过pB调用的non-virtual函数永远都是B所定义的版本,同理pD。另一方面,virtual函数却是动态绑定的,以实现多态。
本条款关键:不要重新定义继承而来的非虚函数
由前面的条款可知:
- 适用于B对象的每一件事,也适用于D对象,因为每个D对象都是一个B对象
- B的derived classes一定会继承mf的接口和实现,因为mf是B的一个non-virtual函数
如果D要重新定义mf,那么“每个D都是一个B”就不为真。既然如此D就不该以public形式继承B。如果要以public方式继承,且D需要实现出与B不同的mf,那么mf就无法为B反映出“不变性凌驾于特异性”的性质,既然如此,mf应该声明为virtual函数。
所以:不论哪一个观点,结论都相同:任何情况下都不该重新定义一个继承而来的non-virtual函数。