C++的三大特性之一的多态是基于虚函数实现的,而大部分编译器是采用虚函数表来实现虚函数,虚函数表(VTAB)存在于可执行文件的只读数据段中,指向VTAB的虚表指针(VPTR)是包含在类的每一个实例当中。当使用引用或指针调用虚函数时,首先通过VPTR找到VTAB,然后通过偏移量找到虚函数地址并调用。

本文参考:1.http://blog.lucode.net/programming-language/cpp-vtab-and-call-convention.html

  2.https://blog.csdn.net/tangaowen/article/details/5830803

  3.《深度探索C++对象模型》

一、单继承

 #include<iostream>
#include <stdio.h>
using namespace std;
class A {
public:
void func() {
cout << "A::func()" << endl;
}
virtual void func1() {
cout << "A::func1(): " << endl;
} virtual void func3() {
cout << "A::func3(): " << endl;
}
}; class B: public A {
public:
virtual void func() {
cout << "B::func()" << endl;
}
virtual void vfunc() {
cout << "B::vfunc()" << endl;
}
void func1() {
cout << "B::func1(): " << endl;
}
};
int main() {
typedef void (*Fun)(void);
B a; Fun *fun = NULL;
fun = (Fun*) ((int *) *(int *) &a);
// fun = *(Fun **) &a;
fun[]();
fun[]();
fun[]();
fun[](); return ;
}

运行结果:

B::func1():
A::func3():
B::func()
B::vfunc()

二、多重继承

 #include<iostream>
#include <stdio.h>
using namespace std;
class B1 {
public:
virtual void barB1() {cout << "B1::bar" << endl;}
virtual void fooB1() {cout << "B1::foo" << endl;}
}; class B2 {
public:
virtual void barB2() {cout << "B2::bar" << endl;}
virtual void fooB2() {cout << "B2::foo" << endl;}
}; class D : public B1, B2 {
public:
void fooB1() {cout << "D::foo" << endl;}
void barB2() {cout << "D::bar" << endl;}
}; typedef void (*Func)();
int main() {
D tt;
Func* vptr1 = *(Func**)&tt;
Func* vptr2 = *((Func**)&tt + ); vptr1[]();
vptr1[]();
vptr1[]();
cout<<"\\\\\\\\\\\\"<<endl;
vptr2[]();
vptr2[](); return ;
}

运行结果:

B1::bar
D::foo
D::bar
\\\\\\
D::bar
B2::foo

结论:

多重继承会有多个虚函数表,几重继承,就会有几个虚函数表。这些表按照派生的顺序依次排列,如果子类改写了父类的虚函数,那么就会用子类自己的虚函数覆盖虚函数表的相应的位置,如果子类有新的虚函数,那么就添加到第一个虚函数表的末尾。

再简单总结一下 覆盖 隐藏 重载 的区别:

覆盖 是C++虚函数的实现原理,基类的虚函数被子类重写,要求函数参数列表相同;

隐藏 是C++的名字解析过程,分两种情况,基类函数有virtual,参数列表不同,或基类函数没有virtual,无论参数列表是否相同。此时基类指针指向基类实例则调用基类函数,指向子类则调用子类函数。

重载 是在同一命名空间中根据参数对同名函数的区别。

// 我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=3ph7kzdx2saoo

C++单继承、多继承情况下的虚函数表分析的更多相关文章

  1. c++ 继承类强制转换时的虚函数表工作原理

    本文通过简单例子说明子类之间发生强制转换时虚函数如何调用,旨在对c++继承中的虚函数表的作用机制有更深入的理解. #include<iostream> using namespace st ...

  2. C++ 虚函数表解析 继承

    C++ 虚函数表解析 陈皓 http://blog.csdn.net/haoel 前言 C++中的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父 ...

  3. C++ 虚函数表 单继承

    本文研究单继承情况下,c++对象的虚函数表的具体情况. 假设有两个类A,B, 其中B由A派生出来,A含有虚函数fun1,B含有虚函数fun2. 测试的代码如下: #include<iostrea ...

  4. C++多态中虚函数表合并与继承问题

    多态: C++的多态是通过一张虚函数表(Virtual Table)来实现的,简称为 V-Table.在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承.覆写的问题,保证其真实反应实际的函数. ...

  5. C++ 由虚基类 虚继承 虚函数 到 虚函数表

    //虚基类:一个类可以在一个类族中既被用作虚基类,也被用作非虚基类. class Base1{ public: Base1(){cout<<"Construct Base1!&q ...

  6. 详谈C++虚函数表那回事(一般继承关系)

    沿途总是会出现关于C++虚函数表的问题,今天做一总结: 1.什么是虚函数表: 虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的.简称为V-Table. ...

  7. C++ 虚函数表与多态 —— 继承的虚函数表 & 内存布局

    1. 使用继承的虚函数表: 如果不涉及多重继承,每个类只有1个虚函数表,当子类继承父类后,子类可以自己改写和新增虚函数,如下图所示: 子类重写 func_1 后,子函数的 func_1 将会有新的逻辑 ...

  8. 异常情况下的Activity生命周期分析

    情况1:资源相关的系统配置发生改变 资源相关的系统配置发生改变,举个栗子.当前Activity处于竖屏状态的时候突然转成横屏,系统配置发生了改变,Activity就会销毁并且重建,其onPause, ...

  9. C++内存中的封装、继承、多态(下)

    上篇讲述了内存中的封装模型,下篇我们讲述一下继承和多态. 二.继承与多态情况下的内存布局 由于继承下的内存布局以及构造过程很多书籍都讲得比较详细,所以这里不细讲.重点讲多态. 继承有以下这几种情况: ...

随机推荐

  1. 【工具】读取proprtties工具类

    获取properties内容: 基本的使用看网络上大多是这样的,使用时注意线程安全以及读写的实时性问题. 1.直接通过流读取(反射): InputStream inStream =  this.get ...

  2. Linux下docker的安装

    前言: 因为之前在自己的mac上直接使用HomeBrew的包管理安装的,使用brew install docker即可,这种方法简单,但最近想尝试在Linux下安装,费了一些时间,主要是启动docke ...

  3. redis整合springboot的helloworld

    引入依赖 compile 'org.springframework.boot:spring-boot-starter-data-redis' 使用redis有两种方法 1.Jedis Jedis je ...

  4. PyCharm2018 汉化&激活

    一.汉化 将下载好的resources_cn_PyCharm_2018.1_r2.jar 放入pycharm 的lib 目录中,启动app即可 下载链接: https://pan.baidu.com/ ...

  5. C++ 洛谷 P2458 [SDOI2006]保安站岗 from_树形DP

    P2458 [SDOI2006]保安站岗 没学树形DP的,看一下. 题目大意:一棵树有N个节点,现在需要将所有节点都看守住,如果我们选择了节点i,那么节点i本身,节点i的父亲和儿子都会被看守住. 每个 ...

  6. sql锁的类型介绍:悲观锁,乐观锁,行锁,表锁,页锁,共享锁,排他锁,意向锁

    1 悲观锁,乐观锁 悲观锁:顾名思义,很悲观,就是每次拿数据的时候都认为别的线程会修改数据,所以在每次拿的时候都会给数据上锁.上锁之后,当别的线程想要拿数据时,就会阻塞,直到给数据上锁的线程将事务提交 ...

  7. Modbus RTU 介绍

    S7-1200 Modbus RTU 通信概述 Modbus具有两种串行传输模式:分别为ASCII和RTU.Modbus是一种单主站的主从通信模式,Modbus网络上只能有一个主站存在,主站在Modb ...

  8. async与await详解

    async和await只是编译器功能.编译器会用Task类创建代码.如果不适用这两个关键字,也可以用C#4.0和Task类实现同样的功能,只是没有那么方便. 题主在概念上确实混淆的不行,但是确实asy ...

  9. nio原理和示例代码

    我正在为学习大数据打基础中,为了手撸rpc框架,需要懂得nio的原理,在搞懂nio框架前,我会带着大家手撸一些比较底层的代码,当然今后当我们学会了框架,这些繁琐的代码也就不用写了,但是学一学底层的代码 ...

  10. js 控制文本框输入要求

    把输入框中 输入的字符串含有中文逗号 改成 英文逗号 举例: <input type="text" id="keywords" style="w ...