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

微信公众号: 星点课堂

新浪微博:女儿叫老白

网易云课堂:女儿叫老白

网易云课堂免费课程:《C++跨平台开发中的编译错误》

网易云课堂免费课程:《C++老鸟日记》

----------------------------------------------------------------------------

(chapter 7 PPT)

引言:

----------------------------------------------------------------------------

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

 

正文:

----------------------------------------------------------------------------

       我们先来看一段代码,

// 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()都被隐藏。

隐藏说完了,我们来说一下覆盖:

// parent.h

class CParent {

public:

  virtual void f(int)

  {

     cout << "CParent::f(int)" << endl;

   }

};

 

// child.h

class CChild : public CParent

{

public:

  void f(int)

  {

    cout << "CChild::f(int)" << endl;

  }

};

 

int main(int argc, 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. 重载的作用就是同一个函数有不同的行为,

不在一个域中的函数是无法构成重载的,这个是重载的重要特征。

相同的函数名的函数,在基类和派生类中的关系只能是覆盖或者隐藏。

 

结语:

----------------------------------------------------------------------------

       重载是在一个域中,同名函数之间的关系。也就是类内部的接口之间的关系。

       覆盖和隐藏指不同域之间的关系。就是基类和派生类之间。

覆盖指派生类的同名同参数接口覆盖了基类的同名同参数的虚函数,也就是编译器优先找派生类同名接口,隐藏指派生类的同名接口隐藏了基类的同名接口。

猜你喜欢

转载自blog.csdn.net/baizy77/article/details/82784220