多态及虚函数表

1.多态

1.1多态的定义

多态即多种形态,C + + 的多态分为静态多态和动态多态。
1.  静态多态就是重载,因为是在编译期决议确定,所以称为静态多态。
2.   动态多态就是通过继承重写基类的虚函数实现的多态,因为是在运行时决议确定,所以称为动态多态

1.2多态实现

- 虚函数重写
 - 对象指针或引用(当父类指针/引用指向父类对象时调用的是父类的虚
函数,指向子类对象时调用的是子类的虚函数。)

2.探索虚函数表

2.1虚函数表定义

定义:
虚函数表是通过一块连续内存来存储虚函数的地址。
这里的地址并非函数真正的地址,而是call指令跳转后的地址。

这张表解决了继承、虚函数(重写)的问题。在有虚函数的对象实例中都存在一张虚函数表,虚函数表就像一张地图,指明了实际应该调用的虚函数函数。

2.2虚表的本质

虚表的本质:
虚表是一种数据结构,实质是一个指针数组,存放虚函数的地址,最后一个元素为0.

3.使用内存窗口验证虚表的存在

class Base
{
public : 
    virtual void func1()
    {
        cout<<"Base::func1" <<endl;
    }

    virtual void func2()
    {
        cout<<"Base::func2" <<endl;
    }

private :
    int a;
};

class Derive :public Base
{
public :
    virtual void func1()
    {
        cout<<"Derive::func1" <<endl;
    }

    virtual void func3()
    {
        cout<<"Base::func3" <<endl;
    }

    int _d;
};

int main()
{
    Base b;
    Derive d;
    system("pause");
    return 0;
}

这里写图片描述

这里派生类继承了父类的虚函数表,并重写了func1函数,那么Derive::func3去哪了呢?(其实他就放在func1后面,这是vs的bug,vs将其处理了)
注意虚函数指针_vfptr后面还有一个0哦!通过内存查看

这里写图片描述

所谓眼见不一定为实,我们如何看到func3的地址呢,及其存在的位置呢?
接下来我们实现地址的显示:

4.探索单继承对象模型

typedef void(*VFUNC)();
void PrintVTable(int* vtable)//若想实现跨平台型可使用int** ,因为32为平台下,指针大小为4字节,64为平台下,指针大小8字节
{
    printf("vtable:0x%p\n", vtable);
    for (int i = 0; vtable[i] != 0; i++)
    {
        printf("vtable[%d]:0x%p->", i, vtable[i]);
        VFUNC f = (VFUNC)vtable[i];
        f();
    }
    cout << endl;
}
class Base
{
public : 
    virtual void func1()
    {
        cout<<"Base::func1" <<endl;
    }

    virtual void func2()
    {
        cout<<"Base::func2" <<endl;
    }

private :
    int a;
};

class Derive :public Base
{
public :
    virtual void func1()//重写
    {
        cout<<"Derive::func1" <<endl;
    }

    virtual void func3()
    {
        cout<<"Base::func3" <<endl;
    }

    int _d;
};

int main()
{
    Base b;
    Derive d;
    PrintVTable((int*)(*((int*)&b)));
    PrintVTable((int*)(*((int*)&d)));

    system("pause");
    return 0;
}

这里写图片描述

这里写图片描述

5.探索多重继承的内存布局

5.1非菱形继承


typedef void(*VFUNC)();
void PrintVTable(int* vtable)
{
    printf("vtable:0x%p\n", vtable);
    for (int i = 0; vtable[i] != 0; i++)
    {
        printf("vtable[%d]:0x%p->", i, vtable[i]);
        VFUNC f = (VFUNC)vtable[i];
        f();
    }
    cout << endl;
}
class Base1
{
public:
    virtual void func1()
    {
        cout << "Base1::func1" << endl;
    }

    virtual void func2()
    {
        cout << "Base1::func2" << endl;
    }

private:
    int b1;
};
class Base2
{
public:
    virtual void func1()
    {
        cout << "Base2::func1" << endl;
    }

    virtual void func2()
    {
        cout << "Base2::func2" << endl;
    }

private:
    int b2;
};
class Derive :public Base1,public Base2
{
public:
    virtual void func1()
    {
        cout << "Derive::func1" << endl;
    }

    virtual void func3()
    {
        cout << "Base::func3" << endl;
    }

    int d;
};

int main()
{
    Base1 b1;
    Base2 b2;
    Derive d1;

    PrintVTable((int*)(*(int*)&b1));
    PrintVTable((int*)(*(int*)&b2));

    PrintVTable((int*)(*(int*)&d1));
    PrintVTable((int*)(*((int*)((int)&d1 + sizeof(Base1)))));

    system("pause");
    return 0;
}

这里写图片描述

这里写图片描述

5.2菱形继承

typedef void(*VFUNC)();
void PrintVTable(int* vtable)
{
    printf("vtable:0x%p\n", vtable);
    for (int i = 0; vtable[i] != 0; i++)
    {
        printf("vtable[%d]:0x%p->", i, vtable[i]);
        VFUNC f = (VFUNC)vtable[i];
        f();
    }
    cout << endl;
}

class A
{
public:
    virtual void f1()
    {}

    virtual void f2()
    {}

    int _a;
};


class B : virtual public A //虚继承
{
public:
    virtual void f1()
    {}

    //virtual void f3()
    //{}

    int _b;
};

class C : virtual public A//虚继承
{
public:
    virtual void f1()
    {}

    //virtual void f3()
    //{}

    int _c;
};

class D : public B, public C
{
public:
    virtual void f1()
    {
        cout<<"D:f1()"<<endl;
    }

    virtual void f4()
    {
        cout<<"D:f4()"<<endl;
    }

    int _d;
};

int main()
{
    D d;
    cout<<sizeof(A)<<endl;
    cout << sizeof(B) << endl;
    cout << sizeof(C) << endl;
    cout << sizeof(D) << endl;



    d._a = 1;
    d._b = 2;
    d._c = 3;
    d._d = 4;
    PrintVTable((int*)(*((int*)&d)));
    system("pause");
    return 0;
}

这里写图片描述
这里写图片描述

注意:

 1.  虚函数虚继承
若为虚继承,子类的虚表内容只包含自己的虚函数指针,把父类的虚函数指针放在了公共区,并把这个公共区的地址放在了对象中。
2.  菱形继承
(1)有几个“含有虚表的父类”,子类就有几个虚表
(2)有几个“西继承同一个类的父类”,自雷就有几个虚基表指针
(3)子类还包括一个“虚函数虚继承产生的公共区域的指针”

猜你喜欢

转载自blog.csdn.net/kai29/article/details/80042116