Item 36:不要重写继承来的非虚函数
不要重写继承来的非虚函数
Derived 继承自 Base。如果 Base有一个非虚函数 func,那么客户会倾向认为下面两种调用结果是一样的:
Derived d;
Base* pb = &d;
Derived* pd = &d;
//@ 以下两种调用应当等效
pb->func();
pd->func();
然而重写非虚函数 func 将会造成上述调用结果不一致:
class Base{
public:
void func(){}
};
class Derived: public Base{
public:
void func(){} //@ 隐藏了父类的名称func
};
因为 pb 类型是Base*
,pd 类型是Derived*
,对于普通函数 func 的调用是静态绑定的(在编译期便决定了调用地址偏移量)。 总是会调用指针类型定义中的那个方法。即 pb->func() 调用的是 Base::func,pd->func() 调用的是 Derived::func。
在子类中重写父类的非虚函数在设计上是矛盾的:
- 父类定义了普通函数 func,意味着它反映了父类的不变式。子类重写后父类的不变式不再成立,因而子类和父类不再是"is a"的关系。
- 如果 func 应当在子类中提供不同的实现,那么它就不再反映父类的不变式。它就应该声明为 virtual 函数。
总结
- 绝不要重定义一个通过继承得到的非虚函数。