浅析C++重载覆盖隐藏以及虚函数

来源:互联网 发布:上海网络广播 编辑:程序博客网 时间:2024/05/21 22:21

C++里面重载覆盖隐藏的概念经常被提及,在很多C++测试题目里面也一直有遇到,每次做这类型的题目总感觉没有彻底搞清楚,故在这里做一个总结,以后遇到类似的问题能够轻易解决。首先来看看<<高质量C/C++编程指南>>作者对几个概念的描述:

重载:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual关键字可有可无。
覆盖:
(1)不同的范围(父子类中)
(2)函数名称相同
(3)参数相同
(4)基类必须有virtual关键字
隐藏:
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。

    重载发生在一个类中,覆盖发生在子类重新实现了父类的虚函数。一般来说,只要记住了重载和覆盖,遇到其他情况只要函数同名基本可以判定为隐藏。隐藏顾名思义就是还可以被找出来,而覆盖就不存在了。重载不能仅仅根据返回值不一致来区分两个重载函数。下面看几个例子。

example1:

class base{public:int test(char x){printf("base::test(char) %d \n", x);}virtual int test(int x){printf("base::test(int) %d \n", x);}};class derived: public base{public:int test(char x){printf("derived::test(char) %d \n", x);}int test(int x){printf("derived::test(int) %d \n",x);}virtual int test(float x){printf("derived::test(float) %f \n",x);}};int main(void){derived obj;base*  pobj = &obj;derived* p2obj = &obj;pobj->test((char)(100));    //(1)p2obj->test((char)(100));    //(2)((derived*)pobj)->test((char)(100));//(5)pobj->test(100);    //(3)p2obj->test(100);    //(4)return (0);}

输出:

base::test(char) 100 derived::test(char) 100 derived::test(char) 100 derived::test(int) 100 derived::test(int) 100 

说明:Base类中,因参数类型不相同,两个test的关系为重载。derived类中三个test也为重载关系,其中derived的test(char)隐藏了基类test(char),test(int)覆盖了基类虚函数virtual int test(int)的实现,也可以说在derived的虚函数表里面用自身的test(int)替换了继承而来的test(int)。

    1>pobj->test((char)(100));参数类型为char,pobj类型为base*,基类test(char)与派生类test(char)的关系为派生类隐藏了基类函数。此时pobj指针类型为base*所以此时调用的是基类的test(char)函数,如果此时要调用派生类的test(char),可以把pobj抢转为derived*类型如(5)所示。同理(2)调用的是derived中的函数。

    2>在derived的虚函数表中,derived中test(int)覆盖了继承而来的base中test(int)函数。(3)用基类指针指向派生类,并调用被覆盖的虚函数,发生了动态绑定,显然调用的是派生类中的test(int)。至于(4)指针指向类型为drived*调用的也是test(int)函数。

example2:

class Father  {  public:      void name(){cout<<"Father name"<<endl;}      virtual void call(){cout<<"Father call"<<endl;}  };  class Son:public Father  {  public:      void name(){cout<<"Son name"<<endl;}      virtual void call(){cout<<"Son call"<<endl;}  };  int main()  {      Son *s1 = new Son();      Father *f1 = (Father *)s1;      f1->call();  //son call    (1)    f1->name();  //father name (2)    ((Son*)f1)->call(); //son call    (3)    ((Son*)f1)->name(); //son name    (4)    Father *f2 = new Father();      Son *s2 = (Son*)f2;      s2->call();  //father call (5)    s2->name();  //son name    (6)    ((Father*)s2)->call();//father call(7)      ((Father*)s2)->name();//father name(8)      return 0;  }  

输出:

Son callFather nameSon callSon nameFather callSon nameFather callFather name

说明:son中的name隐藏了father中的name,son中的call覆盖了father中的call。(1)发生了动态绑定输出son call。(2)指针类型是father*因此输出father name。(3)动态绑定与指针实际指向的类型相关而与指针类型无关故输出son call。(4)由于当前指针类型为father*前面(2)输出father name,而这里把指针类型转为son*相当与把隐藏的name函数显式调用,因此输出son name。(5)输出father call。(6)输出son name。(7)输出father call。(8)输出father name。

    小结:如果函数的关系为隐藏,则判别指针(引用)类型(->符号左边的类型),根据指针类型决定调用的是哪个函数。如果函数关系为隐藏,则判断指针具体指向的类型,根据具体指向来判断调用的函数。PS:有关C++虚函数具体解析请看这里 :虚函数表解析。

____________The End。


原创粉丝点击