• [转]C++中重载(overload)、覆盖(override)、隐藏(hide)的区别


    原文地址:http://blog.csdn.net/Last_Time/archive/2009/11/15/4812520.aspx

    1.重载(overload)
         我们在C中常常需要两个或多个函数完成相同的任务,但是参数的数据类型不同比如large(int i ,int  j),large(float i,float j)所以第一种解决方法就是large_int(),large_float(),但是这种方法比较笨拙,所以我们要找一种比较高明的方法,很幸运C++给我们提供了这样的一种机制—重载。我在同一个程序中可以使用同名的若干函数,但是当然要有限制啦,编译器可不比人聪明,我们要让编译器能唯一区分这些同名函数—函数签名。

      我们先看看如何区分两个同名函数:

        1.参数个数不同
          2.参数个数相同,但是至少有一双对应的函数的类型不同。
      例如:void large(int x,int y) 和void large(int x,int y,int z)不同
            :void large(int x,int y)和 void large(float x,float y)也不同
      我们再来看下一种情况:
            :void large(int x,int y)和int large(int x,int y)
      好像这两个函数可以放到一个程序中,可是事实上当你把这两个函数放到一个程序的时候编译器就糊涂啦,调用一个large(3,4)我到底该不该返回值呢?其实学习过汇编语言的人都知道:我们在调用一个子程序的时候只需要存储的是需要的参数,返回地址。跟们与返回值没关系。所以返回值是完全不能区分两个函数名相同的函数滴。
      我们上边说的就是函数签名的内涵了,现在说说函数签名的概念吧,没办法,有许多人总喜欢问什么是***,总喜欢把一些很抽象的东西概念化,既然不能改变就随波逐流吧.
      函数签名:函数的名称及其参数类型组合在一起就定义了一个唯一的特性,称为函数签名。程序中的每一个函数都必须有一个唯一的函数签名。当编写包含函数调用的语句时,编译器就使用该调用创建一个函数签名,再把它和函数原型或者定义中可用的函数签名集相比较,如果找到了匹配的函数签名就建立索调用的函数,如果没有找到匹配的函数签名,就检查转换参数类型后是否有匹配的函数签名。
      有了这个基础,我们再看看下一种情况:
            :void output(int a,int b=5)和void output(int a)这两个函数能在一个程序中吗?
      相信你一定知道了,答案是“不能”。因为这两个函数的签名相同了,当我调用output(5)时候编译器就糊涂了,两个都可以啊!
      总结一下:重载构成的条件:函数的参数类型,参数个数不同,才能构成函数的重载。但是返回类型不同不能构成函数重载,还要注意函数带有默认参数的情况。

    2.覆盖(override)
      在说覆盖之前我们老规矩还要介绍介绍这个机制推出的背景:
    举个实际的例子,我们编写一个动物类:

     1 #include <iostream>
    2 using namespace std;
    3
    4 class Animal
    5 {
    6
    7 public:
    8 void speak()
    9 {
    10 cout<<"Animal speak"<<endl;
    11 }
    12 };
    13
    14 class Dog:public Animal
    15 {
    16
    17 public:
    18 void speak()
    19 {
    20 cout<<"Dog ::wangwang!"<<endl;
    21 }
    22
    23 };
    24
    25 class Cat:public Animal
    26 {
    27
    28 public:
    29 void speak()
    30 {
    31 cout<<"Cat::miaomiao!"<<endl;
    32 }
    33
    34
    35 };
    36
    37 class Cattle:public Animal
    38 {
    39 public:
    40 void speak()
    41 {
    42 cout<<"Cattle::MouMou!"<<endl;
    43 }
    44
    45
    46 };
    47
    48 int main()
    49
    50 {
    51 Animal* animal;
    52 Dog dog;
    53 Cat cat;
    54 Cattle cattle;
    55
    56 dog.speak();
    57 cat.speak();
    58 cattle.speak();
    59
    60 animal = &dog;
    61 animal->speak();
    62 animal = &cat;
    63 animal->speak();
    64 animal= &cattle;
    65 animal->speak();
    66
    67 return 0;
    68 }

      解释一下这个简单的程序。我定义了一个动物类,狗,猫和牛都继承这个类,并重写了这个speak这个方法,当我dog.speak(),cat.speak(),cattle.speakl()都没有问题,但是这样太麻烦了,于是我想用一个统一的代码来表示这个说的动作,于是我定义了一个指针animal 分别指向dog,cat ,cattle 但是结果并没有我想象的那样,结果如下:

    1 Cat::miaomiao!
    2 Cattle::MouMou!
    3 Animal speak
    4 Animal speak
    5 Animal speak

      这可不是我们所希望的。解释一下出现这个不希望结果的原因:
      当进行animal = &cat的时候,c++编译器进行了类型转换,认为animal中保存的对象就是Animal的对象,当然调用的是Ainmal的speak了。于是聪明的人们就想出了一个解决的办法:编译时不确定具体的调用函数,等到运行时,根据对象的类型来调用时哪一个函数。这个解决问题的能力就是c++提供的多态特性。C++中多态特性是通过哪个关键字来定义的呢?相信你肯定知道答案:virtual. c++的编译器就是根据这个关键字来决定“是在编译时就确定调用哪个函数还是在执行时确定调用哪个函数呢”。编译时确定就称之为“早期绑定(early binding )"执行时确定称为"迟绑定(late binding).好啦,知道了这些知识我们动手改一改刚才的代码吧:其实就是在Animal类的speak方法上加一个关键字virtual. 结果绝对满足我们的预期:

    1 Dog ::wangwang!
    2 Cat::miaomiao!
    3 Cattle::MouMou!
    4 Dog ::wangwang!
    5 Cat::miaomiao!
    6 Cattle::MouMou!

      C++的多态性用一句话来概述:在基类的函数前加virtual关键字,在派生类重写该函数,运行时会根据对象的实际类型来调用相应的函数,如果对象是派生类就调用派生类的函数,如果是基类就调用基类的函数,与指针的类型无关,而是与指针实际指向的对象有关。
      再介绍一个很重要的概念:纯虚函数:纯虚函数是指明位不具体实现的虚成员函数,virtual void breathe() =0;这样就是一个纯虚函数。没有函数体,敖汉纯虚函数的类叫抽象类,如果想继承抽象类就必须要实现父类所有的纯虚函数,否则也要把这个函数在子类写成纯虚函数,这样派生类也成了抽象类,抽象类不能实例化对象。

     

      介绍了一大堆的知识相比你也烦了,但是当我介绍完前面的东西后“覆盖”也就呼之欲出了。
      “覆盖”:基类的虚函数A,有一个派生类重写了A,名称和参数列表与A完全相同,那么就成为函数的“覆盖”。
      看到了吧,所谓的覆盖就是我们前面介绍的speak().基类的speak是虚函数,那么派生类的speak无论是否有virtual关键字,都是虚函数,也就是说你的派生类的speak()前面可以加virtual也可以不加,效果都是一样的。
      总结一下覆盖的条件:
        (1)基类函数必须是虚函数(使用virtual关键字声明)
        (2)发生覆盖的函数要非别在派生类和基类中。
        (3)函数名称和参数列表必须完全相同(不同的话就会引出下一个概念哟)
      那么这次跟返回值有没有关系呢,我实验了一下,当基类有一个虚函数virtual void A(),如果那么派生类中0可能存在int A(),因为编译会报错,但是如果是int A(int x)就可以啦,不过就不是覆盖了。所以再一次验证了根返回值没关系,不要考虑返回值。

    3.隐藏(hide)

      其实前边已经介绍了隐藏了,只不过没有明确说出来,所谓的隐藏就是指在派生类中具有与基类的同名函数(可不管参数列表是不是相同)从而在派生类中隐藏了基类的同名函数。也就是说在派生类调用的一定是派生类的函数,例如基类和派生类都有int A(),在派生类中一定是调用派生类的int A(),但是如果派生类中没有基类的int B()那么在派生类中调用B()时当然是调用基类的B()了,因为派生类中没有嘛,还有一种情况,就是如果基类与派生类的参数列表不同,那么无论基类的函数是不是虚函数基类的函数都会被隐藏。总结一下覆盖的发生条件:1.派生类的函数与基类的函数完全相同,但是基类函数不是虚函数,那么基类的函数将被隐藏。2.当派生类的函数与基类函数同名,但是具有不同的参数列表,那无论基类的函数是不是虚函数都会被隐藏。

      最后总结一下:发生在同一个类的同名函数不同参数列表:重载
        发生在基类和派生类中的函数,基类函数是虚函数,派生类函数与基类函数名称与参数列表完全相同:覆盖
        派生类函数与基类函数名称与参数列表完全相同,但是基类函数不是虚函数:隐藏
        派生类函数与基类函数名称相同但是参数列表不同,无论基类函数是不是virtual:隐藏。

      最后给出一个例子:

     1 #include <iostream>
    2 using namespace std;
    3
    4 class Base
    5 {
    6
    7 public:
    8 virtual void xfn(int i)
    9 {
    10 cout<<"Base::xfn(int i)"<<endl;
    11 }
    12
    13 void yfn(float f)
    14 {
    15 cout<<"Base::yfn(float f)"<<endl;
    16 }
    17 void zfn()
    18 {
    19 cout<<"Base::zfn()"<<endl;
    20 }
    21
    22 };
    23
    24 class Derived :public Base
    25 {
    26 public:
    27 void xfn(int i)//覆盖了xfn的函数
    28 {
    29 cout<<"Derived::xfn(int i)"<<endl;
    30 }
    31
    32 void yfn(int c) //隐藏了基类yfn的函数
    33 {
    34 cout<<"Derived::yfn(int i)"<<endl;
    35 }
    36
    37 void zfn() //隐藏了基类zfn的函数
    38
    39 {
    40 cout<<"Drived::zfn()"<<endl;
    41 }
    42
    43 };
    44
    45
    46 int main()
    47 {
    48
    49 Derived d;
    50
    51 Base* pB = &d;
    52 Derived* pD = &d;
    53
    54 pB->xfn(5);
    55 pD->xfn(5);
    56 pB->yfn(3.14f);
    57 pD->yfn(3.14f);
    58
    59 pB->zfn();
    60 pD->zfn();
    61
    62 return 0;
    63 }

      结果如下:

    1 Derived::xfn(int i)
    2 Derived::xfn(int i)
    3 Base::yfn(float f)
    4 Derived::yfn(int i)
    5 Base::zfn()
    6 Drived::zfn()
  • 相关阅读:
    对svn分支合并类型和深度的理解
    SVN中trunk,branches,tags用法详解
    如何从dump中查找ASP.NET Session的数据【转】
    c++学习笔记
    柳永教授嫖娼案庭审记录
    C++资源之不完全导引(转载)
    不讨老婆之“不亦快哉”(三十三则)(李敖)
    在一个ajax extender 工程中实现多个 ajax extender 控件的方法
    Creating a new extender(zz)
    打标签
  • 原文地址:https://www.cnblogs.com/Clin/p/2281964.html
Copyright © 2020-2023  润新知