避免在派生类中重新定义基类的非虚函数

我们都知道,在基类中定义虚函数的目的是允许派生类拥有相同接口却可以有不同的实现,通过对象的指针或引用来访问虚函数可以实现运行时的多态。这么说来,在派生类中重定义(override)虚函数是没有任何问题的。那么,可以重定义基类中的非虚函数吗?

答案是:技术上你可以这样做(至少编译器不会报错),但是绝大多数情况下不应该做

class Base
{
public:
   void f() { cout << "Base::f()" << endl; }
};

class Derived: public Base
{
public:
   void f() { cout << "Derived::f()" << endl; }
};

int main()
{
   Derived d;
   Base *pb = &d;
   // Base::f()
   pb->f();
   Derived *pd = &d;
   // Derived::f()
   pd->f();   
}
上面的代码有一个诡异的行为:对同一个派生类对象调用同样的函数,返回的结果是不一样的。这是因为普通的非虚函数体在编译期就与函数调用绑定,指针的类型决定了哪个函数被调用。通过Base*调用的f一定是基类的,而通过Derived*调用的f一定是派生类的。

从面向对象的角度来说,在派生类中重定义基类中的非虚函数可能会违反公有继承Is-A的关系。公有继承是说每一个派生类对象都可以被当做基类对象来处理。基类的每一个接口和不变量派生类也有。而非虚函数的具体实现是类不变量的一部分。如果重定义了非虚函数,类不变量就可能被打破,派生类对象将不再是基类对象(当通过指向派生类对象的指针访问f时)。

很少情况下,需要重定义非虚函数。一个特例是为了解决私有继承中的名字隐藏问题。在私有继承中,基类的公有函数在派生类中都是私有,如果派生类想要继承基类的某个接口,可以使用所谓的forwarding function。即通过定义一个与基类一模一样的函数,它的实现仅仅是调用基类的对应函数。注意,使用using语句是不可行的,它会把同名的所有重载函数(f()与f(int))都一起导入派生类,参见:C++名字隐藏对公有继承的影响

class Base
{
public:
  f();
  f(int i);
};

class Derived: private Base
{
public:
  // forwarding function
  f() { Base::f(); }
};

小结:
绝大多数情况下不要重新定义基类的非虚函数,那样会打破公有继承Is-A的关系,而且行为诡异。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值