前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++中函数重载、隐藏、覆盖和重写的区别

C++中函数重载、隐藏、覆盖和重写的区别

作者头像
恋喵大鲤鱼
发布2018-08-03 16:15:13
7.4K0
发布2018-08-03 16:15:13
举报
文章被收录于专栏:C/C++基础C/C++基础

代码编译运行环境:VS2012+Debug+Win32


1.函数重载(Function Overload)

1.1定义

C++规定在同一作用域中,同名函数的形式参数(指参数的个数、类型或者顺序)不同时,构成函数重载。

1.2用法

比如,要从两个变量中返回其中较大的一个值,可以编写如下两个构成重载的函数。

代码语言:javascript
复制
int max(int a,int b){
    return a>b?a:b;
};

double max(double a,double b){
    return a>b?a:b;
}

1.3注意事项

(1)函数返回值类型与构成函数重载无任何关系; (2)类的静态成员函数与普通成员函数可以形成重载; (3)函数重载发生在同一作用域,如类成员函数之间的重载、全局函数之间的重载。

2.函数隐藏(Function Hiding)

2.1定义

函数隐藏指不同作用域中定义的同名函数构成函数隐藏(不要求函数返回值和函数参数类型相同)。比如派生类成员函数屏蔽与其同名的基类成员函数、类成员函数屏蔽全局外部函数。请注意,如果在派生类中存在与基类虚函数同返回值、同名且同形参的函数,则构成函数重写。

2.2用法用例

请仔细研读以下代码。

代码语言:javascript
复制
#include <iostream>
using namespace std;

void func(char* s){
    cout<<"global function with name:"<<s<<endl;
}

class A{
    void func(){
        cout<<"member function of A"<<endl;
    }
public:
    void useFunc(){
        //func("lvlv");//A::func()将外部函数func(char*)隐藏
        func();
        ::func("lvlv");
    }
    virtual void print(){
        cout<<"A's print"<<endl;
    }
};

class B:public A{
public:
    void useFunc(){          //隐藏A::vodi useFunc()
        cout<<"B's useFunc"<<endl;
    }
    int useFunc(int i){      //隐藏A::vodi useFunc()
        cout<<"In B's useFunc(),i="<<i<<endl;
        return 0;
    }
    virtual int print(char* a){
        cout<<"B's print:"<<a<<endl;
        return 1;
    }

    //下面编译不通过,因为对父类虚函数重写时,需要函数返回值类型,函数名称和参数类型全部相同才行
    // virtual int print(){
        // cout<<"B's print:"<<a<<endl;
    // }
};

int main(){
    A a;
    a.useFunc();
    B b;
    b.useFunc();//A::useFunc()被B::useFunc()隐藏
    b.A::useFunc();
    b.useFunc(2);
    //b.print();//编译出错,A::print()被B::print(char* a)隐藏
    b.A::print();
    b.print("jf");
}
代码语言:javascript
复制
程序执行结果: 
member function of A
global function with name:lvlv
B's useFunc
member function of A
global function with name:lvlv
In B's useFunc(),i=2
A's print
B's print:jf

2.3注意事项

对比函数隐藏与函数重载的定义可知: (1)派生类成员函数与基类成员函数同名但参数不同。此时基类成员函数将被隐藏(注意别与重载混淆,重载发生在同一个类中); (2)函数重载发生在同一作用域,函数隐藏发生在不同作用域。

3.函数覆盖与函数重写(Function Override)

网上和很多书籍多都会涉及函数覆盖的概念,众说纷纭,加大了许多初学者的学习难度,甚至产生误导。事实上,函数覆盖就是函数重写。

3.1定义

派生类中与基类同返回值类型、同名和同参数的虚函数重定义,构成虚函数覆盖,也叫虚函数重写。

关于返回值类型存在一种特殊情况,即协变返回类型(covariant return type)。

3.2虚函数重写与协变返回类型

如果虚函数函数返回指针或者引用时(不包括value语义),子类中重写的函数返回的指针或者引用是父类中被重写函数所返回指针或引用的子类型(这就是所谓的协变返回类型)[4]^{[4]}。看示例代码:

代码语言:javascript
复制
#include <iostream>
using namespace std;

class A{}; 
class B:public A{};

class Base{
public:
    virtual A& show(){
        cout<<"In Base"<<endl;
        return *(new A);
    }
};

class Derived:public Base{
public:
     //返回值协变,构成虚函数重写
     B& show(){
        cout<<"In Derived"<<endl;
        return *(new B);
    }
};

3.3注意事项

(1)函数覆盖就是虚函数重写,而不是函数被”覆盖”。 从上面的代码可以看出,函数是不可能被“覆盖”的。有些人可能会错误地认为函数覆盖会导致函数被”覆盖”而”消失”,将不能被访问,事实上只要通过作用域运算符::就可以访问到被覆盖的函数。因此,不存在被”覆盖“的函数。

(2)函数覆盖是函数隐藏的特殊情况。 对比函数覆盖和函数隐藏的定义,不难发现函数覆盖其实是函数隐藏的特例。

如果派生类中定义了一个与基类虚函数同名但参数列表不同的非virtual函数,则此函数是一个普通成员函数(非虚函数),并形成对基类中同名虚函数的隐藏,而非虚函数覆盖(重写)。

《C++高级进阶教程》中认为函数的隐藏与覆盖是两个不同的概念。隐藏是一个静态概念,它代表了标识符之间的一种屏蔽现象,而覆盖则是为了实现动态联编,是一个动态概念。但隐藏和覆盖也有联系:形成覆盖的两个函数之间一定形成隐藏。例如,可以对虚函数采用“实调用”,即尽管被调用的是虚函数,但是被调用函数的地址还是在编译阶段静态确定的,那么派生类中的虚函数仍然形成对基类中虚函数的同名隐藏。

参考如下代码,考察虚函数的实调用和虚调用。

代码语言:javascript
复制
#include <iostream>
using namespace std;

class Base{
public:
    virtual void show(){
        cout<<"In Base"<<endl;
    }
};

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

int main(){
    Base b;
    b.show();
    Derived d;
    d.show();          //对函数show()的实调用
    d.Base::show();    //对函数show()的实调用
    Base *pb=NULL;     
    pb=&d;             
    pb->show();        //对函数show()的虚调用
    pb->Base::show();  //对函数show()的实调用
}

程序运行结果: In Base In Derived In Base In Derived In Base

4.总结

在讨论相关概念的区别时,抓住定义才能区别开来。C++中函数重载隐藏和覆盖的区别,并不难,难就难在没弄清定义,被网上各种说法弄的云里雾里而又没有自己的理解。

在这里,牢记以下几点,就可区分函数重载、函数隐藏、函数覆盖和函数重写的区别: (1)函数重载发生在相同作用域; (2)函数隐藏发生在不同作用域; (3)函数覆盖就是函数重写。准确地叫作虚函数覆盖和虚函数重写,也是函数隐藏的特例。

关于三者的对比,李健老师在《编写高质量代码:改善C++程序的150个建议》给出了较为详细的总结,如下表所示:

三者

作用域

有无virtual

函数名

形参列表

返回值类型

重载

相同

可有可无

相同

不同

可同可不同

隐藏

不同

可有可无

相同

可同可不同

可同可不同

重写

不同

相同

相同

相同(协变)

本文为个人观点,欢迎留言批评指正。


参考文献

[1]陈刚.C++高级进阶教程[M].第一版.武汉:武汉大学出版社,2008:110-P112 [2]百度百科.函数隐藏 [3]李健.编写高质量代码:改善C++程序的150个建议.第一版.北京:机械工业出版社,2012.1:122-125 [4]C++基础:函数重写(override)与协变返回类型(covariant return type)

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2015年11月12日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.函数重载(Function Overload)
    • 1.1定义
      • 1.2用法
        • 1.3注意事项
        • 2.函数隐藏(Function Hiding)
          • 2.1定义
            • 2.2用法用例
              • 2.3注意事项
              • 3.函数覆盖与函数重写(Function Override)
                • 3.1定义
                  • 3.2虚函数重写与协变返回类型
                    • 3.3注意事项
                    • 4.总结
                    • 参考文献
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档