C++中的多态与虚函数的内部实现方法
1、什么是多态
多态性可以简单概括为“一个接口,多种行为”。
也就是说,向不同的对象发送同一个消息, 不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。这是一种泛型技术,即用相同的代码实现不同的动作。这体现了面向对象编程的优越性。
多态分为两种:
(1)编译时多态:主要通过函数的重载和模板来实现。
(2)运行时多态:主要通过虚函数来实现。
2、几个相关概念
(1)覆盖、重写(override)
override指基类的某个成员函数为虚函数,派生类又定义一成员函数,除函数体的其余部分都与基类的成员函数相同。注意,如果只是函数名相同,形参或返回类型不同的话,就不能称为override,而是hide。
(2)重载(overload)
指同一个作用域出生多个函数名相同,但是形参不同的函数。编译器在编译的时候,通过实参的个数和类型,选择最终调用的函数。
(3)隐藏(hide)
分为两种:
1)局部变量或者函数隐藏了全局变量或者函数
2)派生类拥有和基类同名的成员函数或成员变量。
产生的结果:使全局或基类的变量、函数不可见。
3、几个简单的例子
/****************************************************************************************************** * File:PolymorphismTest * Introduction:测试多态的一些特性。 * Author:CoderCong * Date:20141114 * LastModifiedDate:20160113 *******************************************************************************************************/ #include "stdafx.h" #include <iostream> using namespace std; class A { public: void foo() { printf("1\n"); } virtual void fun() { printf("2\n"); } }; class B : public A { public: void foo() //由于基类的foo函数并不是虚函数,所以是隐藏,而不是重写 { printf("3\n"); } void fun() //重写 { printf("4\n"); } }; int main(void) { A a; B b; A *p = &a; p->foo(); //输出1。 p->fun(); //输出2。 p = &b; p->foo(); //输出1。因为p是基类指针,p->foo指向一个具有固定偏移量的函数。也就是基类函数 p->fun(); //输出4。多态。虽然p是基类指针,但实际上指向的是一个子类对象。p->fun指向的是一个虚函数。按照动态类型,调用子类函数 return 0; }
4、运行时多态以及虚函数的内部实现
看了上边几个简单的例子,我恍然大悟,原来这就是多态,这么简单,明白啦!
好,那我们再看一个例子:
class A { public: virtual void FunA() { cout << "FunA1" << endl; }; virtual void FunAA() { cout << "FunA2" << endl; } }; class B { public: virtual void FunB() { cout << "FunB" << endl; } }; class C :public A, public B { public: virtual void FunA() { cout << "FunA1C" << endl; }; }; int _tmain(int argc, _TCHAR* argv[]) { C objC; A *pA = &objC; B *pB = &objC; C *pC = &objC; printf("%d %d\n", &objC, objC); printf("%d %d\n", pA, *pA); printf("%d %d\n", pB, *pB); printf("%d %d\n", pC, *pC); return 0; }
运行结果:
5241376 1563032
5241376 1563032
5241380 1563256
5241376 1563032
细心的同志一定发现了pB出了问题,为什么明明都是指向objC的指针,pB跟别人的值都不一样呢?
是不是编译器出了问题呢?
当然不是!我们先讲结论:
(1)每一个含有虚函数的类,都会生成虚表(virtual table)。这个表,记录了对象的动态类型,决定了执行此对象的虚成员函数的时候,真正执行的那一个成员函数。
(2)对于有多个基类的类对象,会有多个虚表,每一个基类对应一个虚表,同时,虚表的顺序和继承时的顺序相同。
(3)在每一个类对象所占用的内存中,虚指针位于最前边,每个虚指针指向对应的虚表。
先从简单的单个基类说起:
class A { public: virtual void FunA() { cout << "FunA1" << endl; } virtual void FunA2() { cout << "FunA2" << endl; } }; class C :public A { virtual void FunA() { cout << "FunA1C" << endl; } }; int _tmain(int argc, _TCHAR* argv[]) { A *pA = new A; C *pC = new C; typedef void (*Fun)(void); Fun fun= (Fun)*((int*)(*(int*)pA)); fun();//pA指向的第一个函数 fun = (Fun)*((int*)(*(int*)pA) +1); fun();//pA指向的第二个函数 fun = (Fun)*((int*)(*(int*)pC)); fun();//pC指向的第一个函数 fun = (Fun)*((int*)(*(int*)pC) + 1); fun();//pC指向的第二个函数 return 0; }
运行结果:
FunA2
FunA1C
FunA2
int _tmain(int argc, _TCHAR* argv[]) { C objC; A *pA = &objA; B *pB = &objC; C *pC = &objC; typedef void (*Fun)(void); Fun fun = (Fun)*((int*)(*(int*)pC)); fun();//第一个表第一个函数 fun = (Fun)*((int*)(*(int*)pC)+1); fun();//第一个表第二个函数 fun = (Fun)*((int*)(*((int*)pC+1))); fun();<span style="white-space:pre"> </span>//第二个表第一个函数 fun = (Fun)*((int*)(*(int*)pB)); fun();//pB指向的表的第一个函数 return 0; }
哈哈,和我们的猜测完全一致:
FunA2
FunB
FunB
以上就是小编为大家带来的C++中的多态与虚函数的内部实现方法全部内容了,希望大家多多支持猪先飞~
相关文章
- 这篇文章主要介绍了c++中system("pause")的作用和含义,非常不错,具有参考借鉴价值,需要的朋友参考下吧...2020-04-25
基于BootStrap Metronic开发框架经验小结【八】框架功能总体界面介绍
这篇文章主要介绍了基于BootStrap Metronic开发框架经验小结【八】框架功能总体界面介绍 的相关资料,需要的朋友可以参考下...2016-05-14- 这篇文章主要介绍了C# 16 进制字符串转 int的方法,非常不错,具有参考借鉴价值,需要的朋友可以参考下...2020-06-25
InterlliJ IDEA2020新建java web项目找不到Static Web的解决
这篇文章主要介绍了InterlliJ IDEA2020新建java web项目找不到Static Web的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-02- 这篇文章主要介绍了C#判断一个字符串是否是数字或者含有某个数字的方法,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了C#类中static变量用法,实例分析了static变量使用技巧与相关注意事项,需要的朋友可以参考下...2020-06-25
- 这篇文章主要给大家介绍C# winform快捷键设置技巧,涉及到C winform快捷键相关知识,对C winform知识感兴趣的朋友可以参考下本篇文章...2020-06-25
- 最近项目不多忙,于是抽点时间巩固下切换窗口问题,感兴趣的朋友跟着小编一起学习吧...2020-06-25
- 这篇文章主要介绍了C#虚函数用法,实例分析了C#中虚函数的功能与基本使用技巧,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了基于Ionic3实现选项卡切换并重新加载echarts,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-09-24
- 这篇文章主要为大家详细介绍了PC蓝牙通信C#代码实现,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-25
pytorch绘制并显示loss曲线和acc曲线,LeNet5识别图像准确率
今天小编就为大家分享一篇pytorch绘制并显示loss曲线和acc曲线,LeNet5识别图像准确率,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-05-02- 这篇文章主要介绍了C#实现带进度条的ListView 的相关资料,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了SpringMVC文件上传原理及实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-07-15
- 这篇文章主要介绍了C#向线程中传递多个参数的解决方法(两种)的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下...2020-06-25
C# MVC模式中应该怎样区分应用程序逻辑(Controller层)和业务逻辑(Model层)?
这篇文章主要介绍了C# MVC模式中应该怎样区分应用程序逻辑(Controller层)和业务逻辑(Model层)?,这也小编做.NET项目时经常思考和让人混乱的一个问题,这篇文章写的挺好,一下清晰了许多,需要的朋友可以参考下...2020-06-25ShardingSphere jdbc集成多数据源的实现步骤
本文主要介绍了ShardingSphere jdbc集成多数据源的实现步骤,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-10-21- 这篇文章介绍的是利用C#设置自定义文件图标,然后实现双击启动的功能,文章给出了示例代码,介绍的很详细,有需要的可以参考借鉴。...2020-06-25
- 下面小编就为大家带来一篇用C++面向对象的方式动态加载so的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-04-25
基于BootStrap的Metronic框架实现页面链接收藏夹功能按钮移动收藏记录(使用Sortable进行拖动排序)
这篇文章主要介绍了基于BootStrap的Metronic框架实现页面链接收藏夹功能按钮移动收藏记录(使用Sortable进行拖动排序)的相关资料,非常不错,需要的朋友可以参考下...2016-09-01