探索虚函数与多态
来源:互联网 发布:类似于易企秀的软件 编辑:程序博客网 时间: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构造的对象,放置在了最下面,然后有一虚基表指向此处,以后可以访问。
- 探索虚函数与多态
- C++之探索多态的本质(虚函数与虚表)2
- 虚函数相关问题探索
- C++对象布局及多态实现探索之虚函数调用
- 多操作系统探索与实战
- 0053 关于虚继承与虚函数占用字节的探索
- 探索C++对象模型之 多重继承与虚函数表
- 菱形继承与探索多态的原理
- C++对象模型之继承与多态的探索
- x265探索与研究(十):encodeSlice()函数、encodeCTU()函数、encodeCU()函数与finishCU()函数分析
- 探索虚函数表的位置
- 深入探索c++虚函数继承模型
- CFrameWnd中的PreCreateWIndow虚函数探索
- PHP内核探索:Apache运行与钩子函数
- 10.PHP内核探索:Apache运行与钩子函数
- x265探索与研究(六):main()函数
- x265探索与研究(七):encode()函数
- x265探索与研究(九):compressFrame()函数
- "必须搭配使用google play服务才能运行"或“您必须先更新Google Play服务才能运行此应用”-如何安装或更新google play services
- python关键字
- vuejs学习笔记
- 写一个Tomcat+Okhttp实现的聊天websocket聊天框架(一)-- 完成客户端和服务端的通信
- 算法提高 7-2求arccos值
- 探索虚函数与多态
- jzoj P1027【GDOI2005】电路稳定性
- 将博客搬至CSDN
- 10款优秀组件类小程序开发demo推荐
- javascript 第六篇(入门篇)
- 螺旋方阵
- matlab中窗函数的使用(二)
- 界面编写
- java创建删除文件