虚函数表-C++多态的实现原理
参考:http://c.biancheng.net/view/267.html
1、说明
我们都知道多态指的是父类的指针在运行中指向子类,那么它的实现原理是什么呢?答案是虚函数表
在 关于virtual 一文中,我们详细了解了C++多态的使用方式,我们知道没有 virtual 关键子就没法使用多态
2、虚函数表
我们看一下下面的代码
class A
{
public:
int i;
virtual void func() { cout << "A func" << endl; }
virtual void func2() { cout << "A func2" << endl; }
void func3() { cout << "A func3" << endl; }
};
class B : public A
{
int j;
void func() { cout << "B func" << endl; }
void func3() { cout << "B func3" << endl; }
};
int main()
{
cout << sizeof(A) << ", " << sizeof(B); //输出 8,12
return 0;
}
在32位编译模式下,程序的运行结果是:8,12
但是如果把代码中的 virtual 删掉,则程序的运行结果为:4,8
可以发现,有了虚函数之后,类所占的存储空间比没有虚函数多了4个字节,这个4个字节就是实现多态的关键 -- 位于对象存储空间的最前端的指针,存放的是 虚函数表的地址,这个是由编译器实现的
每个带有虚函数的类(包括其子类)都有虚函数表
虚函数表中存放着虚函数的地址,注意是虚函数的地址,非虚函数不在此列
虚函数表是编译器实现的,程序运行时被载入内存,一个类的虚函数表中列出了该类的全部虚函数地址。
例如,上面代码中,类A的对象的存储空间以及虚函数表如图所示:
类B的对象的存储空间以及虚函数表,如下图所示:
多态的函数调用语句被编译成根据基类指针所指向的对象中存放的虚函数表的地址,在虚函数表中查找虚函数地址,并调用虚函数的一系列指令
3、代码示例
在上面代码的基础上
A* p = new B();
p->func(); //B func
p->func3(); //A func3
p->func2(); //A func
第二行代码执行如下:
- 取出 p 指针所指向的位置的前4个字节,即对象所属的类(类B)的虚函数表的地址(64位编译模式下是8个字节);
- 根据虚函数表的地址找到虚函数表,并在虚函数表中查找要调用的虚函数地址;
- 调用虚函数;
到此,我们应该不难理解,上面第二行和第三行代码执行的分别是类A和类B的方法
执行 p->func(); 找的是类B虚函数表中 func() 地址,因为类B重写了,所以保存的是类B的func()地址
而执行 p->func3(); 的时候,发现 func3() 不是虚函数,所以并没有找虚函数列表,而是直接调用的p(类A类型)的方法
同样的,执行 p->func2(); 的时候,找的也是类B的虚函数表,因为类B没有重写 func2,所以存的是类A的虚函数 func2() 的地址,所以执行了类A的 func2() 方法
虚函数表-C++多态的实现原理的更多相关文章
- C++ 虚函数表与多态 —— 虚函数表的内存布局
C++面试经常会被问的问题就是多态原理.如果对C++面向对象本质理解不是特别好,问到这里就会崩. 下面从基本到原理,详细说说多态的实现:虚函数 & 虚函数表. 1. 多态的本质: 形 ...
- C++ 虚函数表与多态 —— 多重继承的虚函数表 & 内存布局
多重继承的虚函数表会有两个虚表指针,分别指向两个虚函数表,如下代码中的 vptr_s_1.vptr_s_2,Son类继承自 Father 和 Mather 类,并且改写了 Father::func_1 ...
- C++ 虚函数表与多态 —— 继承的虚函数表 & 内存布局
1. 使用继承的虚函数表: 如果不涉及多重继承,每个类只有1个虚函数表,当子类继承父类后,子类可以自己改写和新增虚函数,如下图所示: 子类重写 func_1 后,子函数的 func_1 将会有新的逻辑 ...
- C++ 虚函数表与多态 —— 关键字 override 的用法
override 仅能用于虚函数,他属于C++新特性,是重写覆盖的意思,他的存在仅仅是为了提高代码的可阅读性: 作用: 1. 提示程序的阅读者,这个函数是重写父类的功能. 2. 防止程序员在重写父类的 ...
- C++ 虚函数表与多态 —— 关键字 final 的用法
final 字面上最终.最后.不可改变的意思,final 这个关键字在 Jave PHP C++中都有用到,其作用也基本一致. C++中的 final 是C++11新增,他可以用来修饰类,让类无法被继 ...
- C++ 虚函数表与多态 —— 多态的简单用法
首先看下边的代码,先创建一个父类,然后在来一个继承父类的子类,两个类中都有自己的 play() 方法,在代码的第35-37行,创建一个父类指针,然后将子类地址引用赋值给父类,这时调用 P 指针的 pl ...
- 深入剖析C++多态、VPTR指针、虚函数表
在讲多态之前,我们先来说说关于多态的一个基石------类型兼容性原则. 一.背景知识 1.类型兼容性原则 类型兼容规则是指在需要基类对象的任何地方,都可以使用公有派生类的对象来替代.通过公有继承,派 ...
- 从零开始学C++之虚函数与多态(一):虚函数表指针、虚析构函数、object slicing与虚函数
一.多态 多态性是面向对象程序设计的重要特征之一. 多态性是指发出同样的消息被不同类型的对象接收时有可能导致完全不同的行为. 多态的实现: 函数重载 运算符重载 模板 虚函数 (1).静态绑定与动态绑 ...
- C++虚函数表原理
C++中的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指 向其子类的实例,然后通过父类的指针调用实际子类的成员函数.这种技术可以让父类的指针有“多种形态”,这是一种泛型技 ...
随机推荐
- eclips快捷键
所谓"工欲善其事必先利其器",程序写多了,对于快捷键总有些特别的偏爱.在众多编辑器中,Eclipse算是用的比较多,也是最熟的. 最常用(也是最爱的:)) Ctrl+' : 自动 ...
- FPT: Feature Pyramid Transfomer
导言: 本文介绍了一个在空间和尺度上全活跃特征交互(fully active feature interaction across both space and scales)的特征金字塔transf ...
- VNC使用及其常见问题解决方法
博主之前在博文(https://www.cnblogs.com/kangbazi666/p/14153604.html)中已经介绍了多人VNC的配置方法,下面将简单介绍其使用方法及常见问题的解决方法. ...
- Linux 时间同步 02 ntpd、ntpdate的区别
Linux 时间同步 02 ntpd.ntpdate的区别 目录 Linux 时间同步 02 ntpd.ntpdate的区别 [一]这样做不安全. [二]这样做不精确. [三]这样做不够优雅. ntp ...
- 整数划分(硬币问题)(dp)
题目描述 考试时思路 本菜狗考试的时候,第一扁打了纯dfs,15分拿了9分 后面看了时限400ms,多组数据,以为会卡常数,然后就想着先dp打表然后再直接O(1)查询 后面发现自己想多了,数据有点水- ...
- Modbus java slave app
文章实现 Modbus slave app , 用 serial rtu 传输, 代码只实现监听功能(本人测试可行), 要实现写功能,可研究一下代码中 updateProcessImage 方法.完整 ...
- java容器-Iterator
1介绍 java中的容器主要是Collection<E>的子类型,即都实现了Iterable<E>接口,即使是Map类型,也有entrySet方法可以获得该Map的所有元素组成 ...
- B树与B+树区别辨析
我们都知道,innodb中的索引结构使用的是B+树.B+树是一种B树的变形树,而B树又是来源于平衡二叉树.相较于平衡二叉树,B树更适合磁盘场景下文件索引系统.那为什么B树更适合磁盘场景,B+树又在B树 ...
- Java安全之RMI协议分析
Java安全之RMI协议分析 0x00 前言 在前面其实有讲到过RMI,但是只是简单描述了一下RMI反序列化漏洞的利用.但是RMI底层的实现以及原理等方面并没有去涉及到,以及RMI的各种攻击方式.在其 ...
- 70.LeetCode爬楼梯
爬楼梯 点击标题可跳转到官网进行查看 假设你正在爬楼梯.需要 n 阶你才能到达楼顶. 每次你可以爬 1 或 2 个台阶.你有多少种不同的方法可以爬到楼顶呢? 注意:给定 n 是一个正整数. 示例 1: ...