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

总结

  • 绝不要重定义一个通过继承得到的非虚函数。
posted @ 2020-02-26 17:07  刘-皇叔  阅读(501)  评论(0编辑  收藏  举报