探索虚函数与多态

来源:互联网 发布:类似于易企秀的软件 编辑:程序博客网 时间:2024/05/17 01:07


测试环境:win10 64位 vs2013

一些概念

       虚函数:类的成员函数前面加上virtual关键字,则此成员函数即为虚函数。

       重写:在子类定义了一个与父类完全相同的虚函数,则称子类的虚函数重写了父类的虚函数。

       多态:多态就是多种形态,C++的多态分为静态多态和动态多态。静态多态就是重载,因为在编译期间决定调用哪个函数,所以称为静态多态;动态多态是通过继承重写基类的虚函数实现的多态,因为是在运行期间决定调用哪个函数,所以称为动态多态。

1.单继承对象模型

#pragma once#include <iostream>using namespace std;class Base{public:virtual void f1(){cout << "Base::f1" << endl;}virtual void f2(){cout << "Base::f2" << endl;}private:int _a;};class Derive :public Base{public:virtual void f1(){cout << "Derive::f1" << endl;}virtual void f3(){cout << "Derive::f3" << endl;}private:int _b;};typedef void(*FUNC) ();void PrintVTable(int* VTable){cout << "虚表地址:" << VTable << endl;for (int i = 0; VTable[i] != 0; ++i){printf("第%d个虚函数地址 :0%x,->", i, VTable[i]);FUNC f = (FUNC)VTable[i];f();}cout << endl;}void test1(){Base b1;Derive d1;int* VTable1 = (int*)(*(int*)&b1);int* VTable2 = (int*)(*(int*)&d1);PrintVTable(VTable1);PrintVTable(VTable2);}

单继承原理:

       对于b1: 在构造时,为所有虚函数创建一个虚表,虚表首地址存在对象b1的首地址中。

       对于d1: 在构造时,同样要建立一个虚表,由于继承了父类;所以首先构造父类内成员,然后再构造d1内其他成员,在构造父类后,由于存在重写f1。所以将d1中f1覆盖到父类f1位置,以下为测试。

内存分布:

       运行代码发现在监视窗口并没有显示d1完整的虚函数地址,打开内存窗口,输入d1的虚表地址


上述数据基本符合预期,为了更直观,将上述虚函数地址打印,如下:

现在在Base里添加两个Display()函数,一个传参,一个不传,使他们构成重载。

void test2(){Derive d2;Base& b2 = d2;b2.f1();b2.Display();b2.Display(1);}

查看反汇编,明显得出动态多态与静态在寻址时极为不同的方式

b2.f1();               ;动态多态,虚表寻找地址

mov    eax,dword ptr [b2]      ;b2地址给eax

mov    edx,dword ptr [eax]    ;eax指向的内容给edx

mov    esi,esp 

mov    ecx,dword ptr [b2]      ;b2地址给eax

mov    eax,dword ptr [edx]    ;edx内容给eax

call     eax 

cmp    esi,esp 

call    __RTC_CheckEsp (01B1361h) 

b2.Display();      ;静态多态,编译时确定地址

mov    ecx,dword ptr [b2] 

call     Base::Display (01B124Eh) 

b2.Display(1);

push       1 

      b2.Display(1);

mov      ecx,dword ptr [b2] 

call       Base::Display (01B10E1h)

2.多重继承对象模型


#include <iostream>using namespace std;class Base1{public:virtual void f1(){cout<<"Base1::f1"<<endl;}virtual void f2(){cout<<"Base1::f2"<<endl;}private:int _b1;};class Base2{public:virtual void f1(){cout<<"Base2::f1"<<endl;}virtual void f2(){cout<<"Base2::f2"<<endl;}private:int _b2;};class Derive: public Base2,public Base1{public:virtual void f1(){cout<<"Derive::f1"<<endl;}virtual void f3(){cout<<"Derive::f3"<<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 d;PrintVTable((int*)(*((int*)&d)));PrintVTable((int*)(*((int*)((char*)&d + sizeof(Base1)))));}


多重继承原理:

       在重多继承过程中,子类的成员函数重写(上例f1),如果两个父类同时包含重写的函数,将子类的其他虚函数存在优先继承的虚函数表中(上例先继承Base2)。

内存分布:

打开监视窗口仍然发现不能显示全部虚函数的地址,如下:

然后我们可以打印出虚函数地址,如下:

于是我们得到多重继承模型:

3.菱形虚拟继承

#include <iostream>using namespace std;class A{public:virtual void f1(){}int _a;};class B : virtual public A{public:virtual void f1(){}virtual void f2(){cout << "D::f2()" << endl;}int _b;};class C : virtual public A{public:virtual void f1(){}virtual void f2(){cout << "D::f2()" << endl;}int _c;};class D : public B, public C{public:virtual void f1(){cout << "D::f1()" << endl;}virtual void f3(){cout << "D::f3()" << endl;}int _d;};void test1(){D d1;d1._a = 1;d1._b = 2;d1._c = 3;d1._d = 4;PrintVTable((int*)(*(int*)&d1));PrintVTable((int*)(*((int*)&d1 + 3)));//+3是因为用sizeof计算崩溃PrintVTable((int*)(*((int*)&d1 + 7)));//所以对照内存表人工计算}

通过查看内存分配和打印每个虚表内函数地址,画出如下d1模型图,由于虚继承将A构造的对象,放置在了最下面,然后有一虚基表指向此处,以后可以访问














0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 删除了国美安装码怎么办 海信空调保修卡丢了怎么办 海尔空调保修卡丢了怎么办 科龙空调不制冷怎么办 魅族s6屏幕碎了怎么办 格力空调化霜怎么办 一开空调就跳闸怎么办 洗衣机顶盖的安全开关损坏怎么办 苹果平板电脑拍照坏了怎么办 苹果hom键不灵了怎么办 平板电脑home键发烫怎么办 华为平板无法输入资料怎么办 安卓平板没声音怎么办? 平果手机充不了电怎么办 苹果6s用电太快怎么办 平板充不起电了怎么办 日本买的电器国内售后怎么办 海淘地址试投不成功怎么办 怀孕了吐得厉害怎么办 玩网页游戏好卡怎么办 网页游戏占cpu高怎么办 微信有余额绑定其他名下怎么办 银行卡绑定支付余额不对怎么办 可乐机不制冷了怎么办 被淘宝卖家威胁怎么办 征信不好想贷款怎么办 急用钱征信不好怎么办 急用钱逾期不还怎么办 急用钱借款不还怎么办 做b超按压疼痛怎么办 做b超没有尿怎么办 b超憋不到尿怎么办 做b超前没有尿意怎么办 肝胆b超前喝水了怎么办 肝胆彩超喝水了怎么办 胆囊b超喝了水怎么办 系统b超照不到怎么办 思维彩超宝宝不动怎么办 怀孕七个月胎儿缺氧怎么办 怀孕29周小孩偏小怎么办 孕29周胎儿臀位怎么办