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

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

1.函数重载

1.1函数重载的定义

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

1.2函数重载的用法

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

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)类的静态成员函数与与实例成员函数可以形成重载。

2.函数隐藏

2.1函数隐藏的定义

函数隐藏指不同作用域中定义的同名函数构成函数隐藏,比如派生类的成员函数屏蔽了与其同名的基类成员函数,类成员函数屏蔽了全局外部函数。

2.2用法用例

请仔细研读以下代码。

#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(){
        cout<<"B's useFunc"<<endl;
    }
    void useFunc(int i){
        cout<<"In B's useFunc(),i="<<i<<endl;
    }
    virtual void print(char* a){
    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");
}

程序执行结果:
C++中函数重载隐藏和覆盖的区别_第1张图片

1.3函数隐藏的注意事项

对比函数重载与函数隐藏的定义可知:

(1)派生类的成员函数与基类的成员函数同名但参数不同。此时基类的函数将被隐藏(注意别与重载混淆,重载发生在同一个类中);

(2)函数重载发生在同一作用域中,函数隐藏发生在不同作用域中。

3.函数覆盖

网上和很多书籍多都会涉及函数覆盖的概念,众说纷纭,加大了许多初学者的学习难度,甚至产生误导。这里提供两种方法供大家对函数覆盖有个深入的理解。本人倾向第一种理解。

(1)没有函数覆盖,请不要再提这个概念。
从上面的代码可以看出,函数是不可能被“覆盖”的,有些人可能会错误的认为函数隐藏函数覆盖的区别是函数覆盖将不能被访问,事实上只要通过::作用域运算符就可以访问到被隐藏的函数。

因此,不存在被覆盖的函数。

(2)函数覆盖是函数隐藏的特殊情况。

可以给函数覆盖下个定义。函数覆盖指派生类中与基类同返回值类型、同名和同参数的虚函数构成函数覆盖。

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

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

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

#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]陈刚.C++高级进阶教程[M].武汉:武汉大学出版社,2008[3.8(P110-P112)]
[2]百度百科.函数隐藏

你可能感兴趣的:(C++中函数重载隐藏和覆盖的区别)