C++多态中虚函数表合并与继承问题
多态:
C++的多态是通过一张虚函数表(Virtual Table)来实现的,简称为 V-Table。在这个
表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆写的问题,保证其真实
反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,
所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,
它就像一个地图一样,指明了实际所应该调用的函数
这里我们着重看一下这张虚函数表。C++的编译器应该是保证虚函数表的指针存在
于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有
多层继承或是多重继承的情况下)。 这意味着我们通过对象实例的地址得到这张虚函
数表,然后就可以遍历其中函数指针,并调用相应的函数
以下代码运行结果,基于环境 X86_64 64位编译器。
虚函数表与与表指针
#include <iostream>
using namespace std;
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
int ba;
};
typedef void(*FUNC)();
int main()
{
Base b;
cout<<sizeof(b)<<endl;
cout<<"Base_Addr:"<<(int*)(&b)<<endl;
cout<<"VTalbe_Addr:"<<(int**)(int*)(&b)<<endl;
FUNC pf = NULL;
pf = (FUNC)*((int**)*(int*)(&b)+0);
pf();
pf = (FUNC)*((int**)*(int*)(&b)+1);
pf();
pf = (FUNC)*((int**)*(int*)(&b)+2);
pf();
}
运行结果:

虚函数表排在一个类的最前面,虚函数在虚函数表中按声明顺序排列。
派生类部分实现覆写:
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
};
class Derived:public Base
{
public:
virtual void f() { cout<<"Derived::f"<<endl; }
};
typedef void(*FUNC)();
int main()
{
Derived b;
cout<<sizeof(b)<<endl;
cout<<"Base_Addr:"<<(int*)(&b)<<endl;
cout<<"VTalbe_Addr:"<<(int**)(int*)(&b)<<endl;
FUNC pf = NULL;
pf = (FUNC)*((int**)*(int*)(&b)+0);
pf();
pf = (FUNC)*((int**)*(int*)(&b)+1);
pf();
pf = (FUNC)*((int**)*(int*)(&b)+2);
pf();
}
运行结果:

单继承:
父子中兼有虚函数
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
protected:
//int b;
};
class Derived:public Base
{
public:
virtual void d() { cout << "Derived::d" << endl; }
};
typedef void(*FUNC)();
int main()
{
Derived d;
cout<<sizeof(d)<<endl;
cout<<"Base_Addr:"<<(int*)(&d)<<endl;
cout<<"VTalbe_Addr:"<<(int**)(int*)(&b)<<endl;
FUNC pf = NULL;
pf = (FUNC)*((int**)*(int*)(&d)+0);
pf();
pf = (FUNC)*((int**)*(int*)(&d)+1);
pf();
pf = (FUNC)*((int**)*(int*)(&d)+2);
pf(); pf = (FUNC)*((int**)*(int*)(&d)+3);
pf(); Base *pb = & d;
pf = (FUNC)*((int**)*(int*)(pb)+3);
pf();
}
运行结果:


虚函数表形式

总结:单继承体系中,派生类继承基类,并且有自己独立的虚函数,基类与派生类的虚函数表顺序存放在一张表中,派生类的虚函数表在基类后面,类中的成员变量不影响虚函数表的排布。
多继承:
基类中未完全覆写,派生类中又有虚函数
#include <iostream>
using namespace std;
class Base1 {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
};
class Base2 {
public:
virtual void f2() { cout << "Base::f2" << endl; }
virtual void g2() { cout << "Base::g2" << endl; }
virtual void h2() { cout << "Base::h2" << endl; }
};
class Derived:public Base1,public Base2
{
public:
void f() { cout << "Derived::f" << endl; } //override
virtual void i() { cout << "Derived::i" << endl; }
};
typedef void(*FUNC)();
int main()
{
Derived d;
cout<<sizeof(d)<<endl;
Base1 * p1d = &d;
Base2 * p2d = &d;
cout<<"p1d:"<<p1d<<endl;
cout<<"p2d:"<<p2d<<endl;
FUNC pf = NULL;
pf = (FUNC)*((int**)*(int*)(p1d)+0);
pf();
pf = (FUNC)*((int**)*(int*)(p1d)+1);
pf();
pf = (FUNC)*((int**)*(int*)(p1d)+2);
pf();
pf = (FUNC)*((int**)*(int*)(p1d)+3);
pf();
cout<<"-------------------------"<<endl;
pf = (FUNC)*((int**)*(int*)(p2d)+0);
pf();
pf = (FUNC)*((int**)*(int*)(p2d)+1);
pf();
pf = (FUNC)*((int**)*(int*)(p2d)+2);
pf();
// pf = (FUNC)*((int**)*(int*)(p2d)+3); //crash// pf();
}
运行结果

继承关系:

虚函数表:

结论:多继承中,每个基类都有自己的虚函数表,他们是独立存放的,如果派生类中有虚函数,那么虚函数表将与第一个继承的基类虚函数表顺序存放(合并)。
C++多态中虚函数表合并与继承问题的更多相关文章
- 关于C++中虚函数表存放位置的思考
其实这是我前一段时间思考过的一个问题,是在看<深入探索C++对象模型>这本书的时候我产生的一个疑问,最近在网上又看到类似的帖子,贴出来看看: 我看到了很多有意思的答案,都回答的比较好,下面 ...
- 【C++ Primer | 15】C++虚函数表剖析①
概述 为了实现C++的多态,C++使用了一种动态绑定的技术.这个技术的核心是虚函数表(下文简称虚表).本文介绍虚函数表是如何实现动态绑定的. C++多态实现的原理: • 当类中声明虚函数时,编译器会 ...
- C++进阶之虚函数表
C++通过继承(inheritance)和虚函数(virtual function)来实现多态性.所谓多态,简单地说就是,将基类的指针或引用绑定到子类的实例,然后通过基类的指针或引用调用实际子类的成员 ...
- C++ 由虚基类 虚继承 虚函数 到 虚函数表
//虚基类:一个类可以在一个类族中既被用作虚基类,也被用作非虚基类. class Base1{ public: Base1(){cout<<"Construct Base1!&q ...
- C++之虚函数表
本文引自:http://songlee24.github.io/blog/2014/09/02/c-plus-plus-jin-jie-zhi-xu-han-shu-biao/ C++通过继承(inh ...
- C++虚函数表和对象存储
C++虚函数表和对象存储 C++中的虚函数实现了多态的机制,也就是用父类型指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数,这种技术可以让父类的指针有"多种形态",这 ...
- c++基础之虚函数表指针和虚函数表创建时机
虚函数表指针 虚函数表指针随对象走,它发生在对象运行期,当对象创建的时候,虚函数表表指针位于该对象所在内存的最前面. 使用虚函数时,虚函数表指针指向虚函数表中的函数地址即可实现多态. 虚函数表 虚函数 ...
- C++虚函数和虚函数表
前导 在上面的博文中描述了基类中存在虚函数时,基类和派生类中虚函数表的结构. 在派生类也定义了虚函数时,函数表又是怎样的结构呢? 先看下面的示例代码: #include <iostream> ...
- C++ 类的存储方式以及虚函数表
一.C++成员函数在内存中的存储方式 用类去定义对象时,系统会为每一个对象分配存储空间.如果一个类包括了数据和函数,要分别为数据和函数的代码分配存储空间.按理说,如果用同一个类定义了10个对象,那么就 ...
随机推荐
- 团队开发day07
开始整合项目,测试登录,注册,搜索功能, 在安卓中数据处理存在个别错误,功能逻辑有个别不正确 进行修改和完善,添加二次确认退出
- 【剑指offer】28. 对称的二叉树
剑指 Offer 28. 对称的二叉树 知识点:二叉树:递归 题目描述 请实现一个函数,用来判断一棵二叉树是不是对称的.如果一棵二叉树和它的镜像一样,那么它是对称的. 示例 输入:root = [1, ...
- js循环修改对象内层元素的值
问题:存在一个对象,该对象的内部元素也为对象,子对象的元素也为对象,...(即多层对象构成的对象,具体如下),那么应该如何修改最内层元素的值(如 obj.a.a.a = 5)? var obj = { ...
- Matplotlib和Seaborn演示Python可视化
数据可视化:就是使用图形图表等方式来呈现数据,图形图表能够高效清晰地表达数据包含的信息. Seaborn是基于matplotlib,在matplotlib的基础上进行了更高级的API封装,便于用户可以 ...
- Python脚本:删除文件夹下的重复图片,实现图片去重
近期在整理相册的时候,发现相册中有许多重复图片,人工一张张筛查删除太枯燥,便写下这个脚本,用于删除文件夹下重复的图片. 第一部分:判断两张图片是否相同 要查找重复的图片,必然绕不开判断两张图片是否相同 ...
- Intouch/ifix关于语音报警的一种设置思路
工控项目最近升级改造,需要使用Intouch/ifix提供一个语音报警功能.这个不像先前提供的单一的声音报警,业主方要求能详细的提供某某水泵或者是某某设备故障报警,这就要求我们这边对语音解析或者基础控 ...
- 【GCC编译器】Swing Modulo Scheduling
1. SMS 在 GCC 中的实现 1.1. 一些基本概念 (1)软流水(Software pipelining )是一种通过重叠不同迭代的指令,使其并行执行,从而改进循环中指令调度的技术.关键思想是 ...
- VS2019 +MySQL+EntityFramework 使用配置与坑点避免随记
一.安装运行环境 首先我们到mysql的官方网站上下载 mysql-installer-community-8.0.26.0 ,或者其他特定版本,通过它我们可以先将 mysql-for-visuals ...
- 用Matlab求解微分方程
用Matlab求解微分方程 解微分方程有两种解,一种是解析解,一种是数值解,这两种分别对应不同的解法 解析解 利用dsolve函数进行求解 syms x; s = dsolve('eq1,eq2,.. ...
- awk-07-IO和printf语句
IO语句 1.getline 2.getline var 把a文件的行,追加到b文件的结尾 把 a 文件的行替换 b 文件的指定字段 把 a 文件的行替换 b 文件的对应字段 3.command | ...