多态+多态对象模型

来源:互联网 发布:mysql安装教程mac 编辑:程序博客网 时间:2024/06/04 19:50

多态的实现条件
多态(与对象有关 到对象的虚表中找到)=动态联编+虚函数重写
动态联编(运行时决议):指针/引用+虚函数
静态联编(编译时决议)与类型有关,像函数重载和通过对象名调用虚函数

1.定义一个父类的指针,如果这个指针指向父类,调用父类的虚函数,指针指向子类,调用子类的虚函数,这样可以使一个函数有不同的实现,即多态的体现。
2.根据模板的不同实例,实现不同类型的使用。

用一个例代码更好地理解

class A{public:     virtual void f1()    {        cout<<"A::f1()"<<endl;    }     void f2()     {         cout<<"A::f2()"<<endl;     }      void f3()     {         cout<<"A::f3()"<<_a<<endl;     }private:      int _a;};int main(){    A* p = NULL;    //p->f1();(运行不通过,此处动态联编,寻找虚表)    p->f2();//静态联编,不是虚函数,f2存在于代码段中,直接可以调用    //p->f3();//(静态联编,this指针解引用后成空)    return 0;}

虚析构函数
这里我们先来清楚一个问题,为什么最好把父类的析构函数定义成虚函数呢?

#include<iostream>using namespace std;class A{public:     virtual~A()    {        cout<<"~A()"<<endl;    }};class B:public A{public:    ~B()    {        cout<<"~B()"<<endl;    }};int main(){    A* p=new B;    delete p;    return 0;}

现象:给父类加上virtual后,会先析构子类的析构函数,再析构父类的析构函数。

分析:把父类的析构函数声明成虚函数,这将使所有派生类的析构函数自动成为虚函数。这样,如果想要显式地通过delete删除一个对象,delete的操作对象用了指向派生类对象的基类指针,则会调用基类的析构函数。

探索虚函数表
通过一块连续内存来存储虚函数的地址。
这里写图片描述
正如上图中监视显示,虚函数表_vfptr中存放着虚函数fun1和fun2的地址。
单继承有虚函数但是没有重写虚函数
这里写图片描述
通过监视窗口可以看到,父类中有一个虚函数,父类的实例化对象中有一个虚表指针和一个成员变量,然后子类中有一个函数,但不是虚函数,但为什么子类的成员变量里会有一个虚表指针呢?因为它是从父类里继承而来的。需要说明的是,这个函数指针,指向的内容是父类的虚函数。
单继承有虚函数有重写虚函数

class A{public:    virtual void show()    {        cout<<"A show()"<<endl;    }public:    int _a;};class B:public A{public:    void show()    {        cout<<"B show()"<<endl;    }    void show1()    {        cout<<"B show()"<<endl;    }public:    int _b;};int main(){    A a;    B b;    return 0;}

这里写图片描述
通过和上面的继承虚函数相比,子类的虚函数表中的函数地址已经变成了子类的函数了。
探索单继承对象模型

class Base{public:    virtual void fun1()    {        cout<<"Base::fun1"<<endl;    }    virtual void fun2()    {        cout<<"Base::fun2"<<endl;    }private:    int a;};class Derive:public Base{public:    virtual void fun1()    {        cout<<"Derive::fun1"<<endl;    }    virtual void fun3()    {        cout<<"Derive::fun3"<<endl;    }    virtual void fun4()    {        cout<<"Derive::fun4"<<endl;    }private:    int b;};void Test(){    Base b1;    Derive d1;}

对于Derive类来说,我们预想的是,它的虚表里会有子类重写父类的fun1,父类的fun2,子类的fun3和fun4.
这里写图片描述

但是,通过监视窗口可知只有fun1()和fun2()。这里是因为编译器的问题所以没有显示出fun3和fun4。
所以接下来我们需要实现一个可以打印虚函数表的函数。

typedef void(*FUNC) ();void PrintVTable(int* VTable){    cout<<"虚表地址"<<VTable<<endl;    for(int i=0;VTable[i]!=0;++i)    {        printf("低%d个虚函数地址:0X%x->",i,VTable[i]);        FUNC f=(FUNC) VTable[i];//转为指针类型        f();    }    cout<<endl;}void Test(){    Base b1;    Derive d1;    int* VTable1=(int*)(*(int*)&b1);    int* VTable2=(int*)(*(int*)&d1);    PrintVTable(VTable1);  PrintVTable(VTable2);//显式打印}

这里写图片描述
函数的实现:
这里写图片描述
这里还有一点不好理解,就是传参问题

这里写图片描述
探索多继承的内存布局
学习了单继承对象模型,接下来我们看看多继承,同样,通过代码调试观察监视从而了解多继承的内存布局。

class Base1{public:    virtual void fun1()    {        cout<<"Base::fun1"<<endl;    }    virtual void fun2()    {        cout<<"Base::fun2"<<endl;    }private:    int b1;};class Base2{public:    virtual void fun1()    {        cout<<"Base::fun1"<<endl;    }    virtual void fun2()    {        cout<<"Base::fun2"<<endl;    }private:    int b2;};class Derive:public Base1,public Base2{public:    virtual void fun1()    {        cout<<"Derive::fun1"<<endl;    }    virtual void fun3()    {        cout<<"Derive::fun3"<<endl;    }private:    int d1;};typedef void(*FUNC) ();void PrintVTable(int* VTable){    cout<<"虚表地址"<<VTable<<endl;    for(int i=0;VTable[i]!=0;++i)    {        printf("低%d个虚函数地址:0X%x->",i,VTable[i]);        FUNC f=(FUNC) VTable[i];//转为指针类型        f();    }    cout<<endl;}void Test(){    Derive d1;    int* VTable=(int*)(*(int*)&d1);    PrintVTable(VTable);    VTable = (int*)(*((int*)&d1+sizeof(Base1)/4));    PrintVTable(VTable);//显式打印}int main(){    Test();    return 0;}

多继承中有两个虚函数表,分别是Base1和Base2的虚函数表,我们猜想子类里的fun1()会覆盖父类Base1和Base2中的fun1(),但是子类里的fun3()会放在Base1里还是Base2里还是自己开辟一个虚函数表呢?
接下来,我们通过监视窗口看看:
这里写图片描述
结果证实了子类里的fun1()会覆盖父类Base1和Base2中的fun1(),但是,同单继承中出现的问题一样,编译器依旧无法在监视窗口中显示出fun3()的情况。
所以,靠自己吧!
这里写图片描述
通过上图的打印结果可知,fun3()存在于Base1的虚函数表中。当涉及多继承时,子类的虚函数会存放于先继承的那个类的虚函数表里。
这里写图片描述

原创粉丝点击