Virtual、虚函数、指针、 引用、基类、父类

来源:互联网 发布:全国道路矢量数据 编辑:程序博客网 时间:2024/05/16 19:27

面试C++工程师碰到虚函数题目的概率为99%,昨天面试又碰到一个更经典的问题,先将这些整理,大家共同学习。

<span style="font-size:18px;"><span style="font-size:18px;">#include <iostream>using namespace std;class Base{public:    int base;    Base()    {        Virfun2();//好经典的问题        cout<<"Construct Base Object"<<endl;    }    void fun1()    {        cout<<"Call Base fun1() "<<endl;    }    virtual void Virfun2()    {        cout<<"Call Base Virfun2() "<<endl;    }    ~Base()//最好用Virtual,不然析构会达不到想要的效果    {        cout<<"Destruct Base Object"<<endl;    }};class SonA:public Base{public:    int sona;    SonA()    {        cout<<"construct SonA Object"<<endl;    }    void fun1()    {        cout<<"Call SonA fun1() "<<endl;    }    void Virfun2()    {        cout<<"Call SonA Virfun2() "<<endl;    }    ~SonA()    {        cout<<"Destruct SonA Object"<<endl;    }};void baseF1(Base *b1){    b1->fun1();    b1->Virfun2();}void sonAF2(SonA *s1){    s1->fun1();    s1->Virfun2();}void referSonAF3(Base &rs1){    rs1.fun1();    rs1.Virfun2();}int main(){    Base *bpb = new Base();    Base *bps = new SonA();    SonA *spA = new SonA();    SonA sAo;    Base bo = sAo;    SonA sAo1;    Base &referBo = sAo;    bpb->fun1();    bpb->Virfun2();    ((SonA*)bpb)->fun1();    ((SonA*)bpb)->Virfun2();    bps->fun1();    bps->Virfun2();    ((SonA*)bps)->fun1();    ((SonA*)bps)->Virfun2();    ((Base*)spA)->fun1();    ((Base*)spA)->Virfun2();    bo.fun1();    bo.Virfun2();    referBo.fun1();    referBo.Virfun2();    baseF1(bpb);    baseF1(bps);    baseF1(spA);    sonAF2((SonA*)bpb);    sonAF2((SonA*)bps);    sonAF2(spA);    referSonAF3(sAo);    delete bpb;    delete bps;    delete spA;    return 0;}</span></span>
可以先试着把输出结果写出来,看看与运行结果是否相同。

对应输出如下:


<span style="font-size:18px;"><span style="font-size:18px;">#include <iostream>using namespace std;class Base{public:    int base;    Base()    {        Virfun2();//好经典的问题        cout<<"Construct Base Object"<<endl;    }    void fun1()    {        cout<<"Call Base fun1() "<<endl;    }    virtual void Virfun2()    {        cout<<"Call Base Virfun2() "<<endl;    }    ~Base()    {        cout<<"Destruct Base Object"<<endl;    }};class SonA:public Base{public:    int sona;    SonA()    {        cout<<"construct SonA Object"<<endl;    }    void fun1()    {        cout<<"Call SonA fun1() "<<endl;    }    void Virfun2()    {        cout<<"Call SonA Virfun2() "<<endl;    }    ~SonA()    {        cout<<"Destruct SonA Object"<<endl;    }};void baseF1(Base *b1){    b1->fun1();    b1->Virfun2();}void sonAF2(SonA *s1){    s1->fun1();    s1->Virfun2();}void referSonAF3(Base &rs1){    rs1.fun1();    rs1.Virfun2();}int main(){    //Output    Base *bpb = new Base();    //Call Base Virfun2()    //Construct Base Object    Base *bps = new SonA();//Q1    //Call Base Virfun2()    //Construct Base Object    //construct SonA Object    SonA *spA = new SonA();    //Call Base Virfun2()    //Construct Base Object    //construct SonA Object    SonA sAo;    //Call Base Virfun2()    //Construct Base Object    //construct SonA Object    Base bo = sAo;//Q2    SonA sAo1;    //Call Base Virfun2()    //Construct Base Object    //construct SonA Object    Base &referBo = sAo;//只是对象sAo内存块的别名,没有给它分配内存哦,所以既不要构造也不要析构    bpb->fun1();    //Call Base fun1()    bpb->Virfun2();    //Call Base Virfun2()//不解释了,基类指针指向基类对象稳稳的基类的虚函数    ((SonA*)bpb)->fun1();    //Call SonA fun1()//Q3    ((SonA*)bpb)->Virfun2();    //Call Base Virfun2()//Q3    bps->fun1();    //Call Base fun1()    bps->Virfun2();    //Call SonA Virfun2()//最常见的,也是传说中的,多态    ((SonA*)bps)->fun1();//一般指针只认最后自己什么指针类型,就指向哪种类型函数,所以是SonA的。    //Call SonA fun1()    ((SonA*)bps)->Virfun2();//强制转换对虚函数没有改变,所以之前是SonA的,现在还是。    //Call SonA Virfun2()    ((Base*)spA)->fun1();    //Call Base fun1()    ((Base*)spA)->Virfun2();    //Call SonA Virfun2()    bo.fun1();    //Call Base fun1()    bo.Virfun2();    //Call Base Virfun2()    referBo.fun1();    //Call Base fun1()    referBo.Virfun2();    //Call SonA Virfun2()//引用的效果和指针的效果相同哦    baseF1(bpb); //上面的都理解的话,下面只不过是用函数实现都是一样的。    //Call Base fun1()    //Call Base Virfun2()    baseF1(bps);    //Call Base fun1()    //Call SonA Virfun2()    baseF1(spA);    //Call Base fun1()    //Call SonA Virfun2()    sonAF2((SonA*)bpb);    //Call SonA fun1()    //Call Base Virfun2()    sonAF2((SonA*)bps);    //Call SonA fun1()    //Call SonA Virfun2()    sonAF2(spA);    //Call SonA fun1()    //Call SonA Virfun2()    referSonAF3(sAo);    //Call Base fun1()    //Call SonA Virfun2()        delete bpb;    //Destruct Base Object    delete bps;//Q4    //Destruct Base Object    delete spA;    //Destruct SonA Object    //Destruct Base Object    /*对sAo1析构*/    //Destruct SonA Object    //Destruct Base Object    /*对bo析构*/    //Destruct Base Object//bo虽然没有调用构造函数(调用了拷贝构造函数),但是也要析构,不要忘记哦    /*对sAo析构*/    //Destruct SonA Object    //Destruct Base Object    return 0;}</span></span>

具体输出结果如下:

<span style="font-size:18px;">Call Base Virfun2()Construct Base ObjectCall Base Virfun2()Construct Base Objectconstruct SonA ObjectCall Base Virfun2()Construct Base Objectconstruct SonA ObjectCall Base Virfun2()Construct Base Objectconstruct SonA ObjectCall Base Virfun2()Construct Base Objectconstruct SonA ObjectCall Base fun1()Call Base Virfun2()Call SonA fun1()Call Base Virfun2()Call Base fun1()Call SonA Virfun2()Call SonA fun1()Call SonA Virfun2()Call Base fun1()Call SonA Virfun2()Call Base fun1()Call Base Virfun2()Call Base fun1()Call SonA Virfun2()Call Base fun1()Call Base Virfun2()Call Base fun1()Call SonA Virfun2()Call Base fun1()Call SonA Virfun2()Call SonA fun1()Call Base Virfun2()Call SonA fun1()Call SonA Virfun2()Call SonA fun1()Call SonA Virfun2()Call Base fun1()Call SonA Virfun2()Destruct Base ObjectDestruct Base ObjectDestruct SonA ObjectDestruct Base ObjectDestruct SonA ObjectDestruct Base ObjectDestruct Base ObjectDestruct SonA ObjectDestruct Base ObjectProcess returned 0 (0x0)   execution time : 0.703 sPress any key to continue.</span>

Q1:Base *bps = new SonA; 后调用了父类的构造函数,构造函数里调用的Virfun2()又是虚函数,为什么没有调用子类的Virfun2()而是调用父类的Virfun2()?

R1:因为创建子对象时,是先调用父类构造函数再调用子类构造函数,所以此时还没有调用子类的构造函数,无法找到子类指向虚函数表的虚指针(一个类若是有虚函数,那么对象将会在前四个字节存储指向虚函数表的指针,虚函数表存放的是虚函数地址)

Q2:Base bo = sAo;这里居然没有输出?

R2:这里调用的是Base类默认的拷贝构造函数而不是我们重写的无参构造函数。

Q3:强制类型转换是怎么玩的?

R3:定义一个父类对象,将该父类对象的指针强制类型转换为子类指针,并赋给一个子类指针,则:该子类指针将指向该父类对象,但是其this指针仍然是指向子类的指针。
当该指针调用虚成员函数时,将按照对象的内存布局(指向虚函数表的指针(存储在该对象的前四个字节))调用父类虚成员函数。
当该指针调用普通成员函数时,将通过this指针调用位于代码段的该子类的成员函数。

Q4:怎么不是先析构子类吗?和想的不一样?

R4:问题就出在了析构函数没有定义为虚函数,当析构函数没有定义为虚函数时,就好比我们的fun1()没有用虚函数一样,无法调用到子类的析构函数。只要定义析构函数为虚函数那么就可以先输出Destruct SonA Object。我想这也是一般要求析构函数定义为虚函数的原因吧。

总结:

1、对于一般函数,调用的哪个类的函数和指针最后的类型一致。

比如:

<span style="font-size:18px;">((SonA*)bpb)->fun1(); //Call SonA fun1()//Q3</span>

2、对于虚函数,在于对象实例化时是哪种类型,和指针当前的类型没有必然关系,并且强制转换指针类型不改变输出结果。

<span style="font-size:18px;">Base *bps = new SonA();bps->Virfun2();//Call SonA Virfun2()//最常见的,也是传说中的,多态</span>
<span style="font-size:18px;">((SonA*)bps)->Virfun2();//强制转换对虚函数没有改变,所以之前是SonA的,现在还是。</span>
3、对象的引用和指针的效果相同。
<span style="font-size:18px;">Base &referBo = sAo;</span>

referBo.Virfun2();//引用的效果和指针的效果相同哦

//Call SonA Virfun2()





1 0
原创粉丝点击