C++中虚函数实现原理揭秘
在任何类中不存在显示的类型信息,可对象中必须存放类信息,否则类型不可能在运行时建立。那这个类信息是什么呢?我们来看下面几个类: class no_virtual
{
public:
void fun1() const{}
int fun2() const { return a; }
private:
int a;
} class one_virtual
{
public:
virtual void fun1() const{}
int fun2() const { return a; }
private:
int a;
} class two_virtual
{
public:
virtual void fun1() const{}
virtual int fun2() const { return a; }
private:
int a;
} 以上三个类中:
no_virtual没有虚函数,sizeof(no_virtual)=4,类no_virtual的长度就是其成员变量整型a的长度;
one_virtual有一个虚函数,sizeof(one_virtual)=8;
two_virtual 有两个虚函数,sizeof(two_virtual)=8; 有一个虚函数和两个虚函数的类的长度没有区别,其实它们的长度就是no_virtual的 长度加一个void指针的长度,它反映出,如果有一个或多个虚函数,编译器在这个结构中插入一个指针( V P T R)。在one_virtual 和 two_virtual之间没有区别。这是因为V P T R指向一个存放地址的表,只需要一个指针,因为所有虚函数地址都包含在这个表中。 这个VPTR就可以看作类的类型信息。 那我们来看看编译器是怎么建立VPTR指向的这个虚函数表的。先看下面两个类:
class base
{
public:
void bfun(){}
virtual void vfun1(){}
virtual int vfun2(){}
private:
int a;
} class derived : public base
{
public:
void dfun(){}
virtual void vfun1(){}
virtual int vfun3(){}
private:
int b;
} 两个类VPTR指向的虚函数表(VTABLE)分别如下:
base类
——————
VPTR——> |&base::vfun1 |
——————
|&base::vfun2 |
——————
derived类
———————
VPTR——> |&derived::vfun1 |
———————
|&base::vfun2 |
———————
|&derived::vfun3 |
———————
每当创建一个包含有虚函数的类或从包含有虚函数的类派生一个类时,编译器就为这个类创建一个VTABLE,如上图所示。在这个表中,编译器放置了在这个类 中或在它的基类中所有已声明为virtual的函数的地址。如果在这个派生类中没有对在基类中声明为virtual的函数进行重新定义,编译器就使用基类 的这个虚函数地址。(在derived的VTABLE中,vfun2的入口就是这种情况。)然后编译器在这个类中放置VPTR。当使用简单继承时,对于每 个对象只有一个VPTR。VPTR必须被初始化为指向相应的VTABLE,这在构造函数中发生。
一旦VPTR被初始化为指向相应的VTABLE,对象就"知道"它自己是什么类型。但只有当虚函数被调用时这种自我认知才有用。 个人总结如下:
1、从包含虚函数的类派生一个类时,编译器就为该类创建一个VTABLE。其每一个表项是该类的虚函数地址。
2、在定义该派生类对象时,先调用其基类的构造函数,然后再初始化VPTR,最后再调用派生类的构造函数( 从二进制的视野来看,所谓基类子类是一个大结构体,其中this指针开头的四个字节存放虚函数表头指针。执行子类的构造函数的时候,首先调用基类构造函数,this指针作为参数,在基类构造函数中填入基类的vptr,然后回到子类的构造函数,填入子类的vptr,覆盖基类填入的vptr。如此以来完成vptr的初始化。 )
3、在实现动态绑定时,不能直接采用类对象,而一定要采用指针或者引用。因为采用类对象传值方式,有临时基类对象的产生,而采用指针,则是通过指针来访问外部的派生类对象的VPTR来达到访问派生类虚函数的结果。 VPTR 常常位于对象的开头,编译器能很容易地取到VPTR的值,从而确定VTABLE的位置。VPTR总指向VTABLE的开始地址,所有基类和它的子类的虚函 数地址(子类自己定义的虚函数除外)在VTABLE中存储的位置总是相同的,如上面base类和derived类的VTABLE中vfun1和vfun2 的地址总是按相同的顺序存储。编译器知道vfun1位于VPTR处,vfun2位于VPTR+1处,因此在用基类指针调用虚函数时,编译器首先获取指针指 向对象的类型信息(VPTR),然后就去调用虚函数。如一个base类指针pBase指向了一个derived对象,那pBase->vfun2 ()被编译器翻译为 VPTR+1 的调用,因为虚函数vfun2的地址在VTABLE中位于索引为1的位置上。同理,pBase->vfun3 ()被编译器翻译为 VPTR+2的调用。这就是所谓的晚绑定。
C++中虚函数实现原理揭秘的更多相关文章
- 20140415 HOG 不同继承方式的访问特性 虚函数工作原理
1.HOG block重叠的好处 由于行人通常其形状可以视为柔体,人 的边缘位置不固定,而有一些移动,block 重叠后,一个边缘的梯度信息在两个相邻重叠的 block 中都能有所表达,这样即使边缘的 ...
- C++中虚函数的作用和虚函数的工作原理
1 C++中虚函数的作用和多态 虚函数: 实现类的多态性 关键字:虚函数:虚函数的作用:多态性:多态公有继承:动态联编 C++中的虚函数的作用主要是实现了多态的机制.基类定义虚函数,子类可以重写该函数 ...
- C++中虚函数的作用浅析
虚函数联系到多态,多态联系到继承.所以本文中都是在继承层次上做文章.没了继承,什么都没得谈. 下面是对C++的虚函数这玩意儿的理解. 一, 什么是虚函数(如果不知道虚函数为何物,但有急切的想知道,那你 ...
- 关于C++与Java中虚函数问题的读书笔记
之前一直用C++编程,对虚函数还是一些较为肤浅的理解.可近期由于某些原因搞了下Java,发现有些知识点不熟,于是站在先驱巨人的肩上谈谈C++与Java中虚函数问题. Java中的虚函数 以下是段别人的 ...
- C++中虚函数的作用是什么?它应该怎么用呢?(转)
虚函数联系到多态,多态联系到继承.所以本文中都是在继承层次上做文章.没了继承,什么都没得谈. 下面是对C++的虚函数这玩意儿的理解. 一, 什么是虚函数(如果不知道虚函数为何物,但有急切的想知道,那你 ...
- C++中虚函数功能的实现机制
要理解C++中虚函数是如何工作的,需要回答四个问题. 1. 什么是虚函数. 虚函数由于必须是在类中声明的函数,因此又称为虚方法.所有以virtual修饰符开始的成员函数都成为虚方法.此时注意是vir ...
- c++中虚函数
虽然很难找到一本不讨论多态性的C++书籍或杂志,但是,大多数这类讨论使多态性和C++虚函数的使用看起来很难.我打算在这篇文章中通过从几个方面和结合一些例子使读者理解在C++中的虚函数实现技术.说明一点 ...
- 【高级】C++中虚函数机制的实现原理
多态是C++中的一个重要特性,而虚函数却是实现多态的基石.所谓多态,就是基类的引用或者指针可以根据其实际指向的子类类型而表现出不同的功能.这篇文章讨论这种功能的实现原理,注意这里并不以某个具体的编译器 ...
- C++ 中的虚函数表及虚函数执行原理
为了实现虚函数,C++ 使用了虚函数表来达到延迟绑定的目的.虚函数表在动态/延迟绑定行为中用于查询调用的函数. 尽管要描述清楚虚函数表的机制会多费点口舌,但其实其本身还是比较简单的. 首先,每个包含虚 ...
随机推荐
- cocos2d-x使用DragonBones动画
前言 在网上查找关于DragonBones在cocos2d-x的使用教程,找了大半天也没有找到一个有用的.在自己摸索了一段时间终于摸索了出来,在这里记下分享给大家. 下载DragonBones 我这里 ...
- Linux命令——监视相关
1.netstat 用于显示各种网络相关信息,如网络连接,路由表,接口状态 (Interface Statistics),masquerade 连接,多播成员 (Multicast Membershi ...
- Percona XtraBackup 备份原理
前言 Percona XtraBackup(简称PXB)是 Percona 公司开发的一个用于 MySQL 数据库物理热备的备份工具,支持 MySQl(Oracle).Percona Server 和 ...
- 《APUE》第三章笔记(2)
read函数 调用read函数从打开的文件中读数据. #include <unistd.h> ssize_t read(int filedes, void *buf, size_t nby ...
- bootstrap Tooltip换行问题
bootstrap自身带有tooltip,使用起来很方便,但是美中不足,它的tooltip并不支持换行. 比如我们通过<textarea>输入框传入到数据库的长文本,文本是带有换行符的,但 ...
- IIS日志分析
发现一个强大的图形化IIS日志分析工具——Log Parser Studio,下面分享一个实际操作案例. 1. 安装Log Parser Studio a) 需要先安装Log Parser,下载地址: ...
- zzuli oj 1145 有问题的里程表 2
Description 某辆汽车有一个里程表,该里程表可以显示一个整数,为该车走过的公里数.然而这个里程表有个毛病:它总是从3变到5,而跳过数字4,里程表所有位(个位. 十位.百位等)上的数字都是如此 ...
- 使用 windows 计划任务播放音乐文件
这个问题网上可以搜到很多答案,但都有一些小细节没有交代,而我平时又很少使用计划任务,所以配置中出了点问题,特此备注. 1.播放器 检查当前系统下目标文件的默认播放器是什么,并且确保可以运行. 比如首次 ...
- Docker入门
-----------------------------------------Docker入门教程(一)介绍Docker入门教程(二)命令Docker入门教程(三)DockerFileDocker ...
- 向SDE图层中添加大量数据时,出现ORA-00604以及ORA-01000的解决办法
转自原文 向SDE图层中添加大量数据时,出现ORA-00604以及ORA-01000的解决办法 写了一个小程序,从一个列表中读取坐标串,每个坐标串生成一个IPolygon,然后将这些Polygon添加 ...