本篇内容介绍了“C/C++多态原理实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!面向对象编程有三大特性:继承、封装和多态。其中,多态又分为编译时多态和运行时多态。编译多态是通过重载函数体现的,运行多态是通过虚函数体现的。多态是如何实现的呢?下面举个例子:结果:在例子中由于Base类中 fun1 和 fun2 函数签名不同(其中,函数后面是否有const 也是签名的一部分),从结果分析实现重载,体现了多态性。Base为基类,其中的函数为虚函数。子类1继承并重写了基类的函数,子类2继承基类但没有重写基类的函数,从结果分析子类体现了多态性。那么为什么会出现多态性,其底层的原理是什么?这里需要引出一些相关的概念来进行解释。虚表:虚函数表的缩写,类中含有virtual关键字修饰的方法时,编译器会自动生成虚表虚表指针:在含有虚函数的类实例化对象时,对象地址的前四个字节存储的指向虚表的指针父类对象模型:子类对象模型:上图中展示了虚表和虚表指针在基类对象和派生类对象中的模型,下面阐述实现多态的过程:(1)编译器在发现基类中有虚函数时,会自动为每个含有虚函数的类生成一份虚表,该表是一个一维数组,虚表里保存了虚函数的入口地址(2)编译器会在每个对象的前四个字节中保存一个虚表指针,即vptr,指向对象所属类的虚表。在构造时,根据对象的类型去初始化虚指针vptr,从而让vptr指向正确的虚表,从而在调用虚函数时,能找到正确的函数(3)所谓的合适时机,在派生类定义对象时,程序运行会自动调用构造函数,在构造函数中创建虚表并对虚表初始化。在构造子类对象时,会先调用父类的构造函数,此时,编译器只“看到了”父类,并为父类对象初始化虚表指针,令它指向父类的虚表;当调用子类的构造函数时,为子类对象初始化虚表指针,令它指向子类的虚表(4)当派生类对基类的虚函数没有重写时,派生类的虚表指针指向的是基类的虚表;当派生类对基类的虚函数重写时,派生类的虚表指针指向的是自身的虚表;当派生类中有自己的虚函数时,在自己的虚表中将此虚函数地址添加在后面这样指向派生类的基类指针在运行时,就可以根据派生类对虚函数重写情况动态的进行调用,从而实现多态性。下面在VS2019环境下,通过程序展现:代码部分:运行结果:整个程序图示:通过图示我们可以看出,函数在构造后,通过vptr寻找到vtbl,进而得到所对应的成员函数。而它是怎么做到寻找到所需要的是父类还是子类的成员函数呢?这里就要提到另一个隐藏的指针,this指针。this指针是隐藏在类里面的一个指针,它指向当前对象,通过它可以访问当前对象的所有成员。如程序中如果出现: C c;
c.vfunc1();其实编译器会对其进行处理,从直观上可以将 vfunc1() 看作是下面形式(不知编译器是否这样转换): c.A::vfunc1(&c);其中,&c就是隐藏的this指针,通过this指针,进而得到c对象需要的成员函数。同时,这里面还包括另一个C++语法:动态绑定和静态绑定静态绑定:绑定的是静态类型,所对应的函数或属性依赖于对象的静态类型,发生在编译期;动态绑定:绑定的是动态类型,所对应的函数或属性依赖于对象的动态类型,发生在运行期;从上面的定义也可以看出,非虚函数一般都是静态绑定,而虚函数都是动态绑定(如此才可实现多态性)。所以,我们在上面代码中加入一些代码如下: B bb;
A aa = (A)bb;
aa.vfunc1();同时,加入断点,进行调试,通过vs201免费云主机域名9窗口查看反汇编代码,我们得到如下代码: B bb;
00B63237 lea ecx,[bb]
00B6323D call B::B (0B6129Eh)
A aa = (A)bb;
00B63242 lea eax,[bb]
00B63248 push eax
00B63249 lea ecx,[aa]
00B6324F call A::A (0B6128Ah)
aa.vfunc1();
00B63254 lea ecx,[aa]
00B6325A call A::vfunc1 (0B6111Dh) 由于,aa是一个A的对象而非指针,即使a内容是B对象强制转换而来,aa.vfunc1()调用的是静态绑定的A::vfunc1()。同时,在汇编中我们得到,在调用时,直接call xxxx,call后面是一个固定的地址,从这里依旧可以看出是静态绑定。同时,我们继续运行下面代码: A* pa = new B;
pa->vfunc1(); pa = &b;
pa->vfunc1();得到如下反汇编: A* pa = new B;
00B6325F push 10h
00B63261 call operator new (0B6114Fh)
00B63266 add esp,4
00B63269 mov dword ptr [ebp-174h],eax
00B6326F cmp dword ptr [ebp-174h],0
00B63276 je __$EncStackInitStart+68Fh (0B6328Bh)
00B63278 mov ecx,dword ptr [ebp-174h]
00B6327E call B::B (0B6129Eh)
00B63283 mov dword ptr [ebp-17Ch],eax
00B63289 jmp __$EncStackInitStart+699h (0B63295h)
00B6328B mov dword ptr [ebp-17Ch],0
00B63295 mov eax,dword ptr [ebp-17Ch]
00B6329B mov dword ptr [pa],eax
pa->vfunc1();
00B632A1 mov eax,dword ptr [pa]
00B632A7 mov edx,dword ptr [eax]
00B632A9 mov esi,esp
00B632AB mov ecx,dword ptr [pa]
00B632B1 mov eax,dword ptr [edx]
00B632B3 call eax
00B632B5 cmp esi,esp
00B632B7 call __RTC_CheckEsp (0B61316h) //并非固定地址 pa = &b;
00B632BC lea eax,[b]
00B632BF mov dword ptr [pa],eax
pa->vfunc1();
00B632C5 mov eax,dword ptr [pa]
00B632CB mov edx,dword ptr [eax]
00B632CD mov esi,esp
00B632CF mov ecx,dword ptr [pa]
00B632D5 mov eax,dword ptr [edx]
00B632D7 call eax
00B632D9 cmp esi,esp
00B632DB call __RTC_CheckEsp (0B61316h) 在下面这段程序中,我们可以看到,指针pa指向一个B对象,有一个向上转型操作,可以确定,这应该是动态绑定。同时,在汇编代码中,call后面并不是一个固定的地址,从这里我们也可以看出pa调用了B::vfunc1()。“C/C++多态原理实例分析”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注百云主机网站,小编将为大家输出更多高质量的实用文章!
本文小编为大家详细介绍“es2017是es8吗”,内容详细,步骤清晰,细节处理妥当,希望这篇“es2017是es8吗”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。 es2017是es8。es全称“ECMAScript”,是根据ECM…
免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。