多态的对象模型

多态的原理

#include<iostream>
using namespace std;
class A{
public:
     ~A(){
        cout << "A::~A()" << endl;
    }
protected:
    int _a;
};
class B:public A
{
public:
    ~B(){
        cout << "B::~B()" << endl;
    }
protected:
    int _B;
};
int main(){
    A* p1 = new A;
    delete p1;
    A* p2= new B;
    delete p2;
}

此时只有父类指针但是没有构成虚函数的重写,所以是隐藏(重定义),那么想要做到指向父类调父类指向子类调子类,只能调的动父类的析构函数,除非显示的指明类域。我们来看看汇编代码
这里写图片描述
再加上virtual后,就会发现汇编代码有所变化
这里写图片描述
这主要就是虚表的原因,所以我们就来研究研究就虚表(虚函数表)
这里写图片描述
虚函数表的指针存放在对象的前四个字节上,通过虚表指针能够找到虚函数表,有了virtual关键字后,AB两个类的析构函数构成了重写,所以B继承了A就会覆盖A的析构函数。

研究虚函数表

多态就是有多种形态,满足两个条件1、虚函数的重写2、父类的引用或者指针才能构成多态,形成多态以后,就能实现父类指针/引用指向父类对象时调用父类的虚函数,指向子类对象是调用子类的虚函数。

1、虚函数的存储是怎样的?
虚函数表是通过一块连续的内存来存储虚函数的地址。因为一旦编译好就给虚函数分配好空间,形成虚表,此后就不会再改变,所以虚表一般存储在常量区*,而且虚表相当于一个函数指针数组,以0结尾,*虚表指针存放在对象头上四个字节处。
这里写图片描述
来画一下对象模型:我们验证存放虚函数表地址的位置在对象的前四个字节这里写图片描述可以看到确实是的,那么对象模型就好画了
9

下面我们分单继承和多继承来探究虚函数
1、单继承

这里写图片描述
单从监视窗口来看虚表是不完整的,因为虚函数存放在虚表中,而在这里可以看到func1被覆盖成了Derive的虚函数,但是看不到子类自己的那些虚函数,所以我们来打印一下虚函数表看看!!!
根据虚表是指针数组,所以可以进行一系列操作可以看到虚表

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

    }
    virtual void func2(){
        printf("Base::func2");
    }
private:
    int _a;
};
class Derive :public Base{
public:
    Derive(){
        cout << "D()" << endl;
    }
    virtual void func1(){
        printf("Derive::func1");

    }
    virtual void func3(){
        printf("Derive::func3");
    }
    virtual void func4(){
        printf("Derive::func4");

    }
private:
    int _d;
};
typedef void(*VFUNC)();//声明一个函数指针指向返回值为void无参的一系列虚函数

void PrintVtable(int* Vtable){
    printf("虚表地址:0x%p\n", Vtable);
    for (int i = 0; Vtable[i] != NULL; i++){
        //这里来使用i++的方式来遍历虚函数表,是因为本来里面存放函数指针往后加得是指针的大小
        printf("[%d]:0x%x->", i, Vtable[i]);
        VFUNC f = (VFUNC)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);

}
int main(){
    test();
    system("pause");
    return 0;
}

这里写图片描述
扩展说一下,打印虚表时,分平台在设计打印虚函数表时的操作
这里写图片描述

多继承

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<<"Derive::func3" <<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 Test1 ()

{

Derive d1 ;

int* VTable = (int*)(*( int*)&d1 );

PrintVTable(VTable );

// Base2虚函数表在对象Base1后面 VTable = (int *)(*((int*)&d1 + sizeof (Base1)/4));

PrintVTable(VTable );

}

对象模型
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值