C++中的重载、覆盖、隐藏?

作者: veaxen 分类: C/C++,笔试面试 发布时间: 2017-08-01 13:49

首先说说概念上的区别

重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

重写(覆盖):是指子类重新定义父类虚函数的方法。这里有个需要注意的点,只要父类定义函数时使用了virtual关键字,那么子类没有写virtual关键字也同样是覆盖,而且子类的函数也是虚函数。如下:

virtual void fun(){...}//这是在父类中的定义

......

virtual void fun(){...}//这是在子类中的定义,前面的virtual关键字可有可无

隐藏:是指子类的成员函数或者成员变量隐藏了父类的成员函数或者成员变量,是隐藏了,而不是没有了,实际上被隐藏的函数或者成员变量都还是存在于对象中的,只是单纯通过名字无法访问到,要通过指明类属去访问。隐藏是个比较容易让人头疼的问题,这里我们重点分析隐藏带来的问题。

分析下面的代码:

class A{
public:
    int data = 1;
    void fun(int a){cout<<"fun(a)"<<endl;}      //【1】
    virtual void fun(){cout<<"fun()"<<endl;}    //【2】
};
class B:public A{
public:
    int data = 2;
    void fun(int b){cout<<"fun(b)"<<endl;}      //【3】
};
int main()
{
    B b;
    b.fun();                                    //【4】
    b.fun(1);                                   //【5】
    cout<<b.data<<endl;                         //【6】
    cout<<b.A::data<<endl;                      //【7】
    return 0;
}

这里,A有个数据成员data,B也有个同名的数据成员data,B继承自A,上面代码【6】输出的是2,B类的data把A中的data给隐藏了,我们无法直接通过b.data去访问到A类中的数据成员data,但是我们可以通过指明类属去访问到,例如【7】就通过指明b.A::data去访问A中的data,而不是B中的data。隐藏只是看不见,实际还是存在的,而覆盖就是直接用了一个新的,旧的就没有了

对于【1】【2】,我们称之为重载,只不过【2】同时还是虚函数而已,符合重载的定义。

子类B中的【3】同样隐藏了父类A中的【1】【2】,什么?为什么是隐藏了【1】【2】,而不是【1】?这也是隐藏需要特别注意的地方了,【3】的函数名字和【1】【2】相同,所以就把他们都隐藏掉了,但是【3】并不是虚函数,因为其与【2】的参数不同,不是对【2】的覆盖

看了上面的分析,我们来看【4】,这句代码是编译不通过的,【4】试图去调用父类中的【2】,但是因为【3】隐藏了【1】【2】,所以通过对象b是无法直接访问到【2】的,除非我们指明类属,如b.A::fun()。对于【5】就很简单了,就是调用了B类中的【3】,而子类中的【1】被隐藏了。

看到这里,我们还发现隐藏对覆盖/重载的影响有时候是很让人头疼的,这里考虑一个问题:父类中重载了几个fun,而且它们都是虚函数,那么在子类中只对其中一个进行重写会造成什么问题?
很明显,我们无法通过子类去访问其他未被重写的fun,因为他们都被重写的fun给隐藏了,所以记住,一旦重写了一个fun,也要对其他重载版本的fun进行重写,除非你自己很清楚你不需要他们,或者你清楚的知道通过指明他们的类属去访问

从实现原理上来说

重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:void func(int num);和void func(float f);。那么编译器做过修饰后的函数名称可能是这样的:func_int、func_float。对于这两个函数的调用,在编译器间就已经确定了,是静态的。也就是说,它们的地址在编译期就绑定了(早绑定)。

重写(覆盖):和多态真正相关。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚绑定)。

关于隐藏,是编译器通过名字去计算偏移,而子类的名字和父类重复时,就以子类的为准。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

一条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据