baizy77 发表于 2018-9-20 11:20:32

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

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

版权声明---------------------------------------------------------------------------------------------------------------------该文章原创于Qter开源社区(www.qter.org)作者: 女儿叫老白 (白振勇)转载请注明出处!---------------------------------------------------------------------------------------------------------------------课程目录:《C++老鸟日记》目录本套课程属于:《C++跨平台开发干货》系列课程。-----------------------------------------------------------------------------
引言:----------------------------------------------------------------------------       前面的章节中,我们讨论了重载,今天我们来讨论一下重载、覆盖、隐藏之间有什么区别。
正文:----------------------------------------------------------------------------       我们先来看一段代码,// parent.hclass CParent {public:  void f(int)  {     cout << "CParent::f(int)" << endl;  }};
// child.hclass 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()都被隐藏。隐藏说完了,我们来说一下覆盖:// parent.h
class CParent {
public:
  virtual voidf(int)
  {
     cout<< "CParent::f(int)" << endl;
   }
};

// child.h
class CChild :public CParent
{
public:
  void f(int)
  {
    cout<< "CChild::f(int)" << endl;
  }
};
int main(intargc, char* argv[])
{
       CChild cild;
       child.f(1);
    return 0;
}

我们可以看到,基类定义了一个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. 重载的作用就是同一个函数有不同的行为,不在一个域中的函数是无法构成重载的,这个是重载的重要特征。相同的函数名的函数,在基类和派生类中的关系只能是覆盖或者隐藏。
结语:----------------------------------------------------------------------------       重载是在一个域中,同名函数之间的关系。也就是类内部的接口之间的关系。       覆盖和隐藏指不同域之间的关系。就是基类和派生类之间。覆盖指派生类的同名同参数接口覆盖了基类的同名同参数的虚函数,也就是编译器优先找派生类同名接口,隐藏指派生类的同名接口隐藏了基类的同名接口。

tan 发表于 2018-9-22 11:27:10

老师总结的好,    重载是同一作用域, 覆盖和隐藏是继承关系。    覆盖包含着多态。   
多态是不是。   如果只定义了父类对象, 那么就是调用的父类虚函数。如果定义了子类对象,不管是父类调用和子类调用都是调用的子类覆盖的函数么 ? thank you 。
页: [1]
查看完整版本: C++老鸟日记027 重载、覆盖、隐藏