找回密码
 立即注册
收起左侧

C++老鸟日记027 重载、覆盖、隐藏

1
回复
5211
查看
[复制链接]
累计签到:41 天
连续签到:1 天
来源: 原创 2018-9-20 11:20:32 显示全部楼层 |阅读模式

马上注册,查看详细内容!注册请先查看:注册须知

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
本帖最后由 baizy77 于 2018-10-1 20:58 编辑

版权声明
---------------------------------------------------------------------------------------------------------------------
该文章原创于Qter开源社区(www.qter.org
作者: 女儿叫老白 (白振勇)
转载请注明出处!
---------------------------------------------------------------------------------------------------------------------
本套课程属于:《C++跨平台开发干货》系列课程。
-----------------------------------------------------------------------------

引言:
----------------------------------------------------------------------------
       前面的章节中,我们讨论了重载,今天我们来讨论一下重载、覆盖、隐藏之间有什么区别。

正文:
----------------------------------------------------------------------------
       我们先来看一段代码,
// parent.h
class CParent {
public:
  void f(int)
  {
     cout << "CParent::f(int)" << endl;
  }
};

// child.h
class CChild : public CParent
{
public:
  void f(int,int)
  {
    cout << "CChild::f(int,int)" << endl;
  }

  void test()
  {
   f(1);
  }
};

int main(int argc, char* argv[])
{
  return 0;
}
编译了一下,报错:error C2660: 'f' : function does not take 1 parameters。
错误代码在CChild的test()接口中,在f(1)处。错误指明,函数f()不是只有一个参数。
这就奇怪了,CChild虽然提供了带两个参数的f(int, int),但是它是从CParent派生的,而CParent拥有f(int),这可是只有一个参数啊。
这就涉及到编译器的接口查找规则:在调用一个类的成员函数的时候,编译器会沿着类的继承链逐级的向上查找函数的定义,如果找到了就停止查找。所以如果一个派生类和一个基类都有同一个同名(不论参数是否相同)的函数,编译器最终选择在派生类中的函数。那么我们就说这个派生类的成员函数"隐藏"了基类的成员函数,也就是它阻止了编译器继续向上查找函数的定义。
在本例中,编译器在CChild类中找到了一个f(),虽然这个f()长得是f(int, int)的样子,并不是test()接口中调用的带一个参数的f(),但是编译器仍然会停止查找。它认定找到的这个f(int, int)就是它想要的,因此就报错了。
       这就是隐藏的概念:
1. 在类CChild这个域中,定义了f(int, int)而没有定义f(int)这样的函数,基类中的void f(int)被隐藏
2.  如果把派生CChild中成员函数void f(int,int)的声明改成和基类中一样,即f(int),基类中的void f(int)还是一样被隐藏,此时编译不会出错,
结论:
在基类中的函数,函数名是f(参数是什么我们不管),那么如果在派生类CChild中也声明了某个f()成员函数,那么在类CChild域中,基类中所有的那些f()都被隐藏。
隐藏说完了,我们来说一下覆盖:
  1. // parent.h
  2. class CParent {
  3. public:
  4.   virtual voidf(int)
  5.   {
  6.      cout<< "CParent::f(int)" << endl;
  7.    }
  8. };
复制代码


  1. // child.h
  2. class CChild :public CParent
  3. {
  4. public:
  5.   void f(int)
  6.   {
  7.     cout<< "CChild::f(int)" << endl;
  8.   }
  9. };
  10. int main(intargc, char* argv[])
  11. {
  12.        CChild cild;
  13.        child.f(1);
  14.     return 0;
  15. }
复制代码


我们可以看到,基类定义了一个f(int),派生类也定义了f(int)。但是基类的f(int)前面使用了virtual关键字进行声明,这说明该接口是虚的,派生类可以重新实现。因此,main()函数中,child.f(1)实际上调用了派生类的f(1),这就是覆盖,派生类覆盖了基类的同名(参数也必须完全一致)接口,这本质上是多态的概念。
如果我们把派生类的f(int)改成f(int, int)呢?大家可以自己试一下,结果也是报错:
error C2660: 'f': function does not take 1 parameters。
是不是很面熟啊?这跟前面一样,也是派生类的f(int, int)把基类的f(int)隐藏隐藏了。
那么,隐藏和覆盖到底怎么分辨呢?
如果基类中的函数和派生类中的两个名字一样的函数f满足下面的两个条件:
1. 在基类中函数声明的时候有virtual关键字
2. 基类CParent中的函数和派生类CChild中的函数一模一样,函数名,参数,返回类型都一样。那么这就是叫做覆盖(override),这也就是虚函数,多态的性质。
那么其他的情况呢?只要名字一样,不满足上面覆盖的条件,就是隐藏了。
那么,重载又是怎么回事呢?重载:
1. 必须在一个域中,函数名称相同但是函数参数不同
2. 重载的作用就是同一个函数有不同的行为,
不在一个域中的函数是无法构成重载的,这个是重载的重要特征。
相同的函数名的函数,在基类和派生类中的关系只能是覆盖或者隐藏。

结语:
----------------------------------------------------------------------------
       重载是在一个域中,同名函数之间的关系。也就是类内部的接口之间的关系。
       覆盖和隐藏指不同域之间的关系。就是基类和派生类之间。
覆盖指派生类的同名同参数接口覆盖了基类的同名同参数的虚函数,也就是编译器优先找派生类同名接口,隐藏指派生类的同名接口隐藏了基类的同名接口。


回复

使用道具 举报

累计签到:50 天
连续签到:1 天
2018-9-22 11:27:10 显示全部楼层
老师总结的好,    重载是同一作用域, 覆盖和隐藏是继承关系。    覆盖包含着多态。   
多态是不是。   如果只定义了父类对象, 那么就是调用的父类虚函数。  如果定义了子类对象,不管是父类调用和子类调用都是调用的子类覆盖的函数么 ? thank you 。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了