本文涉及到C++中对象的内存布局知识,若无该方面基础建议先阅读haoel(陈皓)专栏的C++对象内存布局的博客:
http://blog.csdn.net/haoel/article/details/3081328

在拜读上述博客之后,我深受启发,且对C++关于虚函数表的问题有了新的认识和疑惑。比如,在上述博客的最后作者抛出了一个问题:在VS环境下,对象虚函数表指针的下一个字中,存储的不是对象的成员变量,而是一个取值后为-4的地址。这个地址代表什么呢?虚函数表上是如何放置基类和派生类的虚函数呢?

注意:对于在对象中存取虚基类的问题,虚基类表仅是Microsoft编译器的解决办法。在其他编译器中,一般采用在虚函数表中放置虚基类的偏移量的方式。

通过实验,我得到了这样的结论:
1. 一个对象实例只有一个虚函数表,只有一个虚基类表。
2. 对象的每个基类都有一个属于自己的虚函数表指针(vfptr)指向虚函数表(vftbl)的某一项,都有一个属于自己的虚基类表指针(vbptr)指向虚基类表(vbtbl)的某一项。
3. 虚函数表中按照对象继承的顺序排列对象的虚函数地址,虚基类表中按照对象继承的顺序排列对象的直接虚继承类到虚基类的偏移。
4. 当基类无虚函数,且派生类有独立虚函数时,派生类对象起始位置为自己的虚函数表指针。否则派生类的虚函数会归到第一个带虚函数表指针的基类的虚函数表指向范围,这样就节省了一个vfptr的空间。

沿用上述博客的代码:class B
{
public:int ib;char cb;
public:B() :ib(0), cb('B') {}virtual void f() { cout << "B::f()" << endl; }virtual void Bf() { cout << "B::Bf()" << endl; }
};
class B1 : virtual public B
{
public:int ib1;char cb1;
public:B1() :ib1(11), cb1('1') {}virtual void f() { cout << "B1::f()" << endl; }virtual void f1() { cout << "B1::f1()" << endl; }virtual void Bf1() { cout << "B1::Bf1()" << endl; }};
class B2 : virtual public B
{
public:int ib2;char cb2;
public:B2() :ib2(12), cb2('2') {}virtual void f() { cout << "B2::f()" << endl; }virtual void f2() { cout << "B2::f2()" << endl; }virtual void Bf2() { cout << "B2::Bf2()" << endl; }};class D : public B1, public B2
{
public:int id;char cd;
public:D() :id(100), cd('D') {}virtual void f() { cout << "D::f()" << endl; }virtual void f1() { cout << "D::f1()" << endl; }virtual void f2() { cout << "D::f2()" << endl; }virtual void Df() { cout << "D::Df()" << endl; }};
实验方法:int main(void)
{typedef void(*F)();D d;int *op = reinterpret_cast<int*>(&d);    //1    int *vfptr = reinterpret_cast<int*>(*op);//2F fun = reinterpret_cast<F>(*(vfptr));   //3fun();return 0;
}

运行结果为:
D::f1()

将上述语句中3号语句加1呢?
F fun = reinterpret_cast(*(vfptr+1));
运行结果为:
B1::Bf1()

因此说明基类B1的虚函数表指针指向虚函数表挂载着基类B1可调用的虚函数,此时共有3个,指针偏移到3的时候fun的值为0,即VS编译器用来标识的终结。但如果继续增加偏移量呢?

将上述语句中3号语句加4呢?
运行失败,编译器显示此时fun访问一个名为RTTICompleteObjectLocator的类型对象指针。此类型用来进行运行时类型识别,该类型对象存储由编译器生成的特殊类型信息,包括对象继承关系,对象本身的描述。一般放在基类虚函数表之前的一个字中。因此此时fun内存储的是与B2对应的RTTICompleteObjectLocator类型指针。

将上述语句中3号语句加5呢?
运行结果为:
D2::f2()

综上所述,我们可以得到虚函数表的构造:

下一个实验,我们探索虚基类表:int main(void)
{D d;int *op = reinterpret_cast<int*>(&d);        //1int *vbptr = reinterpret_cast<int*>(*(op+1));//2int offset = *vbptr;                         //3std::cout << offset << endl;return 0;
}

运行结果为:
-4

这个-4是什么呢?其实此时vbptr为基类B1的虚基类表指针,位于vfptr的下方(从内存布局角度看是下方,但是地址更高)。对虚基类表的第一项取值得到的就是-4,表示虚基类表指针到基类B1的偏移量。

如果将3号语句的vbptr加1呢?
运行结果为:
40

此时40表示从基类B1的vbptr到B1虚继承的基类B的偏移量。我们按照我引用的文章给的内存布局推算,正好是40个字节。

如果将3号语句的vbptr加2呢?
运行结果为:
0

此处的0也是用来标识B1虚基类表的终结。

如果将3号语句的vbptr加3呢?
运行结果为:
-4

即B2的虚基类表指针到B2对象的偏移量。

如果将3号语句的vbptr加4呢?
运行结果为:
24

即B2的虚基类表指针到其虚继承的基类B对象的偏移量。

综上所述,我们可以得到虚基类表的构造:

注意:测试不同类型数据的时候发现虚函数表上不同类型到基类的offset分布未必相邻!。

c++中虚基类表和虚函数表的布局相关推荐

  1. C++中虚继承产生的虚基类指针和虚基类表,虚函数产生的虚函数指针和虚函数表

    本博客主要通过查看类的内容的变化,深入探讨有关虚指针和虚表的问题. 一.虚继承产生的虚基类表指针和虚基类表 如下代码:写一个棱形继承,父类Base,子类Son1和Son2虚继承Base,又来一个类Gr ...

  2. 【C++】继承和派生、虚继承和虚基类、虚基类表和虚基类指针

    继承和派生.虚继承和虚基类.虚基类表和虚基类指针 继承和派生 继承概述 继承基本概念 派生类中的成员 继承的内容 派生类定义 派生类访问控制 对象构造和析构 对象构造和析构的调用顺序 继承中的构造和析 ...

  3. C++学习 对象模型之虚基类,虚基类表,虚基类表指针

    1.虚基类 什么是虚基类,虚基类的作用是什么? 首先虚基类是为了解决多继承产生的二义性问题,范例代码如下: #include "stdafx.h" #include <std ...

  4. C++虚继承中的虚基类表

    虚继承主要解决多重继承会在子类中存在多份拷贝的问题,这不仅浪费空间,而且存在二义性. 在之前的 C++ 继承中已经说过虚继承基本概念,这里不再赘述.这篇文章主要探究虚继承的原理.文章中多处给出了类实例 ...

  5. Cpp 对象模型探索 / 虚基类表作用

    一.结论 虚基类表的作用是帮助编译器找到该类中的虚基类中各个成员变量在内存布局中的位置. 虚基类表中的值是偏移值,即:各个虚基类的成员变量在子类中的内存布局中相对于虚函数指针的偏移值. 二.栗子 1. ...

  6. c++ 虚函数多态、纯虚函数、虚函数表指针、虚基类表指针详解

    文章目录 静态多态.动态多态 虚函数 哪些函数类型不可以被定义成虚函数? 虚函数的访问方式 析构函数中的虚函数 虚函数表指针 vptr 多继承下的虚函数表 虚基类表指针 bptr 纯虚函数 抽象类 虚 ...

  7. 虚函数,虚基类 与纯虚函数 二

    虚函数    还是先看代码 class A { public:     void funPrint(){cout<<"funPrint of class A"<& ...

  8. 避免在派生类中重新定义基类的非虚函数

    我们都知道,在基类中定义虚函数的目的是允许派生类拥有相同接口却可以有不同的实现,通过对象的指针或引用来访问虚函数可以实现运行时的多态.这么说来,在派生类中重定义(override)虚函数是没有任何问题 ...

  9. 虚函数,虚基类 与纯虚函数 一

    正文 虚基类    在说明其作用前先看一段代码 class A { public:     int iValue; }; class B:public A { public:     void bPr ...

  10. C++中的各种“虚“-- 虚函数、纯虚函数、虚继承、虚基类、虚析构、纯虚析构、抽象类讲解

    C++中的各种"虚" 1. 菱形继承 1.1 虚继承 && 虚基类 1.2 虚基类指针(vbptr)&& 虚基类表(vbtable) 2. 多态 2 ...

最新文章

  1. 2022-2028年中国热熔胶产业竞争现状及发展规模预测报告
  2. 关于Canvas的一些经验
  3. 传输控制协议(TCP)基础知识概述
  4. python可变交换性能优化
  5. 华为 会议室分配时间最长_智慧办公就是现在!华为企业智慧屏系列全新亮相...
  6. 数据挖掘 —— 有监督学习(分类)
  7. 自动化测试用例设计原则
  8. Android入门(三) | Android 的日志工具 Logcat
  9. 为view设置虚线边框
  10. *N#1234567CG,解析征信报告里的暗语
  11. 还被python收智商税?做大数据的朋友告诉我月薪2w的方法
  12. bzoj 3632: 外太空旅行(随机)
  13. POJ 1088 滑雪 (动规)
  14. MATLAB--基本绘图函数
  15. 板卡(单片机)与电脑PING不通的原因及解决方法
  16. contest 12.31
  17. 托福高频真词List17 // 附托福TPO阅读真题
  18. 如何使用 Yahoo! Finance stock API 获取股票数据
  19. 「学习笔记」黑马面面布局开发
  20. UniAPP HBuilderX 运行到各个小程序开发工具

热门文章

  1. SDN的前世今生-SDN是什么
  2. 怎么查微信公众号服务器,微信公众号查询数据库,微信公众号数据库怎么查询?...
  3. Tips:PowerDesigner16.5 图表显示Code以及 Columns新增Commet显示
  4. was升级jdk版本_was升级jdk1.7
  5. Unity开发 解决TapTap隐私协议
  6. excel两列数据对比找不同_Excel找出不同数据
  7. JAVA将图片转成base64导出到word中
  8. 再看2019大前端技术趋势,Web OS概念正落地
  9. 211. 字符串置换
  10. 数据挖掘 NLP 之 文本挖掘 文本处理 通用流程