vtpr的位置:
       为了支持多态,C++引入了vtpr和vtable这两个概念.对于每个有虚函数的类,C++都会为其生成一个vtable,并在类中添加一个隐含的数据成员vptr. 对于vptr在对象中的位置,跟类的数据成员的布局一样,C++标准里面并没有做出任何的规定.但是对于特定的编译器,我们还是可以通过研究C++对象的内存布局来确定vtpr到底是放在哪里.
      下面我们通过分析C++对象的内存布局,来寻找vptr的位置.在开始讨论之前我们先提供一个模板函数void PrintLayout(T const & obj),该函数用于打印obj所在内存的内容,下面是该函数的实现:

PrintLayout.hxx#pragma once
#include <iostream>
#include <iomanip>
#include <ReinterpretCast.hxx>template<typename T>
void PrintLayout(T const & obj)
{int * pObj = ReinterpretCast<int*>(&obj);for (int i =0; i<sizeof(obj)/sizeof(int);++i){std::cout<<std::setw(10)<< pObj[i]<<std::endl;}
}

接下来通过代码来分析一下在C++里,在没有继承,单继承,多继承以及虚继承等情况下对象的内存布局,下面是示例代码,为了减少代码量,我们将类的所有数据成员设为public的,在这里我们用struct来代替class:

//main.cpp#include <iostream>
#include <PrintLayout.hxx>
#include <typeinfo>
using namespace std;struct NoVirtualMemFunc
{int  Func1(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}int  Func2(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}int m_iData;
};
struct Base1
{virtual int  Base1Func1(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}virtual int  Base1Func2(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}int m_iData;
};
struct Base2
{virtual int  Base2Func1(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}virtual int  Base2Func2(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}int m_iData;
};struct D1:public Base1
{virtual int  D1Func(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}int m_iData;
};
struct D:public Base1,public Base2
{virtual int  DFunc(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}int m_iData;
};struct VD1:public virtual Base1
{virtual int  VD1Func(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}int m_iData;
};
struct VD2:public virtual Base1
{virtual int  D2Func(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}int m_iData;
};
struct VD:public VD1,public VD2
{int m_iData;
};template<typename T>
void PRINT_LAYOUT(T const & obj)
{cout<<"The layout of "<<typeid(obj).name()<<"----------------"<<endl;PrintLayout(obj);cout<<endl;
}
int main(int argc, char* argv[])
{//没有继承,没有虚函数的情况{NoVirtualMemFunc obj;obj.m_iData = 100;PRINT_LAYOUT(obj);}//没有继承,有虚函数的情况{Base1 obj;obj.m_iData = 100;PRINT_LAYOUT(obj);}//单继承{D1 obj;obj.Base1::m_iData = 100;obj.m_iData = 200;PRINT_LAYOUT(obj);}//多继承{D obj;obj.Base1::m_iData  = 100;obj.Base2::m_iData  = 200;obj.m_iData = 300;PRINT_LAYOUT(obj);}//虚拟继承{VD1 obj;obj.Base1::m_iData = 100;obj.m_iData = 200;PRINT_LAYOUT(obj);}//棱形继承{VD obj;obj.Base1::m_iData = 100;obj.VD1::m_iData   = 200;obj.VD2::m_iData   = 300;obj.m_iData       = 500;PRINT_LAYOUT(obj);}return 0;
}//输出
/*
The layout of struct NoVirtualMemFunc----------------100The layout of struct Base1----------------4294656100The layout of struct D1----------------4294740100200The layout of struct D----------------42948001004294776200300The layout of struct VD1----------------429487642948882004294864100The layout of struct VD----------------42949444294968200429493242949523005004294920100请按任意键继续. . .

对于有虚表的函数,从上面的输出我们可以得到以下结论,

1.没有继承情况,vptr存放在对象的开始位置,以下是Base1的内存布局

vptr : 4294656

m_iData :100

2.单继承的情况下,对象只有一个vptr,它存放在对象的开始位置,派生类子对象在父类子对象的最后面,以下是D1的内存布局

vptr : 4294740

B1:: m_iData : 100

B2:: m_iData :200

3.多继承情况下,对象会为每个有虚函数的父类子对象提供一个vptr,派生类子对象在所有父类子对象的最后面,所有父类子对象按照声明顺序排列,以下是D的内存布局

B1::vptr : 4294800

B1::m_iData :100

B2::vptr : 4294776

B2::m_iData :200

D::m_iData :300

4. 虚拟继承情况下,虚父类子对象会放在派生类子对象之后,派生类子对象的第一个位置存放着一个vptr,虚拟子类子对象也会保存一个vptr,以下是VD1的内存布局

VD1::vptr :4294876

Unknown : 4294888

VD1::m_iData : 200

B1::vptr :4294864

B1::m_iData :100

5. 棱形继承的情况下,非虚基类子对象在派生类子对象前面,并按照声明顺序排列,虚基类子对象在派生类子对象后面

VD1::vptr :        4294944

VD1::Unknown : 4294968

VD1::m_iData :  200

VD2::vptr :    4   294932

VD2::Unknown : 4294952

VD2::m_iData : 300

VD::m_iData : 500

B1::vptr :       4294920

B1::m_iData :  100

接下来我们将通过代码来验证前面结论的准确性.下面的代码具有一定的局限性.在调试以下代码的时候,对虚拟继承遇到了以下几个让我迷惑的问题:

1.对于虚拟继承,函数指针的大小为12

2.用vtable里面的指针调用,this不能正确传进去

3.如果派生类的虚拟函数多于1个,则会crash

//main.cpp#include <iostream>
#include <GetVptr.hxx>
#include <typeinfo>
using namespace std;struct NoVirtualMemFunc
{int  Func1(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}int  Func2(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}int m_iData;
};
struct Base1
{virtual int  Base1Func1(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}virtual int  Base1Func2(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}int m_iData;
};
struct Base2
{virtual int  Base2Func1(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}virtual int  Base2Func2(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}int m_iData;
};struct D1:public Base1
{virtual int  D1Func(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}int m_iData;
};
struct D:public Base1,public Base2
{virtual int  DFunc(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}int m_iData;
};struct VD1:public virtual Base1
{virtual int  VD1Func(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}int m_iData;
};
struct VD2:public virtual Base1
{virtual int  D2Func(int a,int b){cout<<__FUNCTION__<<"/tm_iData="<<m_iData<<"/ta="<<a<<"/tb="<<b<<endl;return 0;}int m_iData;
};
struct VD:public VD1,public VD2
{int m_iData;
};template<class T>
struct MemFuncT
{typedef int (T::* T_MemFuncT)(int,int);typedef int (T::* T_MemDataT);
};
template<class C>
void CallMemFunc(int iFuncNum,int (C::**vptr)(int,int),C& obj,int a =500,int b =600)
{for (int i =0;i<iFuncNum;++i){//cout<<ReinterpretCast<void*>(vptr[i])<<"   ";(obj.*vptr[i])(a,b);}cout<<endl;
}
int main(int argc, char* argv[])
{//没有继承,有虚函数的情况{cout<<"//没有继承,有虚函数的情况"<<endl;Base1 obj;obj.m_iData = 100;MemFuncT<Base1>::T_MemFuncT * vptr = ReinterpretCast<MemFuncT<Base1>::T_MemFuncT *>(GetVptr(obj));CallMemFunc(2,vptr,obj);}//单继承{cout<<"//单继承"<<endl;D1 obj;obj.Base1::m_iData = 100;obj.m_iData = 200;MemFuncT<D1>::T_MemFuncT * vptr = ReinterpretCast<MemFuncT<D1>::T_MemFuncT *>(GetVptr(obj));CallMemFunc(3,vptr,obj);}//多继承{cout<<"//多继承"<<endl;D obj;obj.Base1::m_iData  = 100;obj.Base2::m_iData  = 200;obj.m_iData = 300;Base1 &objB1 = obj;MemFuncT<Base1>::T_MemFuncT * vptr = ReinterpretCast<MemFuncT<Base1>::T_MemFuncT *>(GetVptr(obj));CallMemFunc(3,vptr,objB1);Base2 &objB2 = obj;MemFuncT<Base2>::T_MemFuncT * vptrB2 = ReinterpretCast<MemFuncT<Base2>::T_MemFuncT *>(GetVptr(objB2));CallMemFunc(2,vptrB2,objB2);}#if 1//虚拟继承{cout<<"//虚拟继承"<<endl;VD1 obj;obj.Base1::m_iData = 100;obj.m_iData = 200;MemFuncT<VD1>::T_MemFuncT * vptr = ReinterpretCast<MemFuncT<VD1>::T_MemFuncT *>(GetVptr(obj));CallMemFunc(1,vptr,obj);Base1 & objB1 =obj ;MemFuncT<Base1>::T_MemFuncT * vptrB1 = ReinterpretCast<MemFuncT<Base1>::T_MemFuncT *>(GetVptr(objB1));CallMemFunc(2,vptrB1,objB1);}//棱形继承{cout<<"//棱形继承"<<endl;VD obj;obj.Base1::m_iData = 100;obj.VD1::m_iData   = 200;obj.VD2::m_iData   = 300;obj.m_iData       = 500;Base1 & objB1 = obj;MemFuncT<Base1>::T_MemFuncT * vptrB1 = ReinterpretCast<MemFuncT<Base1>::T_MemFuncT *>(GetVptr(objB1));CallMemFunc(2,vptrB1,objB1);VD1   & objVD1 =obj;MemFuncT<VD1>::T_MemFuncT * vptrVD1 = ReinterpretCast<MemFuncT<VD1>::T_MemFuncT *>(GetVptr(objVD1));CallMemFunc(1,vptrVD1,objVD1);VD2   & objVD2 =obj;MemFuncT<VD2>::T_MemFuncT * vptrVD2 = ReinterpretCast<MemFuncT<VD2>::T_MemFuncT *>(GetVptr(objVD2));//CallMemFunc(1,vptrVD2,objVD2);}
#endifreturn 0;
}//输出
/*
//没有继承,有虚函数的情况
Base1::Base1Func1       m_iData=100     a=500   b=600
Base1::Base1Func2       m_iData=100     a=500   b=600//单继承
Base1::Base1Func1       m_iData=100     a=500   b=600
Base1::Base1Func2       m_iData=100     a=500   b=600
D1::D1Func      m_iData=200     a=500   b=600//多继承
Base1::Base1Func1       m_iData=100     a=500   b=600
Base1::Base1Func2       m_iData=100     a=500   b=600
D::DFunc        m_iData=300     a=500   b=600Base2::Base2Func1       m_iData=200     a=500   b=600
Base2::Base2Func2       m_iData=200     a=500   b=600//虚拟继承
VD1::VD1Func    m_iData=4294960 a=500   b=600Base1::Base1Func1       m_iData=100     a=500   b=600
Base1::Base1Func2       m_iData=100     a=500   b=600//棱形继承
Base1::Base1Func1       m_iData=100     a=500   b=600
Base1::Base1Func2       m_iData=100     a=500   b=600VD1::VD1Func    m_iData=4295032 a=500   b=600请按任意键继续. . .
*/





深入理解C++对象模型-对象的内存布局,vptr,vtable相关推荐

  1. C++对象模型5——类对象的内存布局

    一.类对象的内存布局 1.1.单一继承的类对象布局 示例1 class base { public:int m_fai;int m_faj; }; class derive : public base ...

  2. java占位符填充_程序员:深入理解Java虚拟机,对象的内存布局

    在 HotSpot 虚拟机中,对象在内存中存储的布局分为 3 块区域:对象头 ( Header ) .实例数据 ( InstanceData ) 和对齐填充 (Padding) . 一.对象的内存布局 ...

  3. C++ 对象的内存布局(上)

    原文地址:http://blog.csdn.net/haoel/article/details/3081328 为尊重原作者的创作成果,所以原文全部内容都会保留,但是会适当添加我的理解. 前言 07年 ...

  4. C++ 对象的内存布局

    C++ 对象的内存布局(上) 陈皓 http://blog.csdn.net/haoel 前言 07年12月,我写了一篇<C++虚函数表解析>的文章,引起了大家的兴趣.有很多朋友对我的文章 ...

  5. c++对象的内存布局2--进阶篇---C++ 对象的内存布局(上)

    目录(?)[-] 前言 对象的影响因素 知识复习 单一的一般继承 多重继承 前言 07年12月,我写了一篇<C++虚函数表解析>的文章,引起了大家的兴趣.有很多朋友对我的文章留了言,有鼓励 ...

  6. C++虚继承(三) --- C++ 对象的内存布局(下)(陈皓)

    C++ 对象的内存布局(下) 陈皓 http://blog.csdn.net/haoel <<<点击这里查看上篇 重复继承 下面我们再来看看,发生重复继承的情况.所谓重复继承,也就是 ...

  7. C++虚继承(二) --- C++ 对象的内存布局(上)(陈皓)

    C++ 对象的内存布局(上) 陈皓 http://blog.csdn.net/haoel 点击这里查看下篇>>> 前言 07年12月,我写了一篇<C++虚函数表解析>的文 ...

  8. JVM从入门到精通(四):内存屏障与JVM指令,对象的内存布局

    JMM 硬件层数据一致性 协议很多,intel 用MESI https://www.cnblogs.com/z00377750/p/9180644.html 现代CPU的数据一致性实现 = 缓存锁(M ...

  9. JVM——深入分析对象的内存布局

    概述 一个对象本身的内在结构需要一种描述方式,这个描述信息是以字节码的方法存储在方法区中的. Class 本身就是一个对象,都以 KB 为单位,如果 new Integer() 为了表示一个数据就占用 ...

最新文章

  1. TimSort算法分析
  2. 关于c语言结构体偏移的一点思考
  3. 【Servlet】Filter过滤器详解、使用示例
  4. kafka 发布-订阅模式_使用Apache Kafka作为消息系统的发布-订阅通信中的微服务,并通过集成测试进行了验证...
  5. Git初学札记(七)————合并分支(merge)
  6. leetcode24题:两两交换链表的节点
  7. 成功演示六要素之三——具体
  8. 多个kinect标定,颜色和深度的标定
  9. 使用FFMPEG合并视频
  10. 网页导出Word几种方法简介
  11. 计算机用通讯电压多少,通信局(站)用交流电源的质量指标要求
  12. Cloudreved云盘搭建及配置Aria2离线下载
  13. 峡谷之巅显示服务器更新,峡谷之巅更新最新资讯
  14. 布袋除尘器有关matlab编程,布袋除尘器工作原理结构图及使用注意事项
  15. 经验谈系列 我们应该怎么给父母配电脑
  16. 2022飞鱼科技-鱼苗夏令营实习-游戏客户端-终面(高管面)已挂
  17. 【杭州SEO优化】网站建设细节分析!
  18. 离散数学20_第1章_等价符号⇔的定义
  19. 日记 [2007年08月29日]
  20. 个人简历模板下载 2020个人简历模板 下载空白简历个人简历

热门文章

  1. JAVA TCP/IP网络通讯编程(二)
  2. 数据结构和算法分析学习笔记(三)--二叉查找树的懒惰删除(lazy deletion)
  3. 1个多月就能看到效果的减肥大法 - 健康程序员,至尚生活!
  4. FormView在什么情况下自动生成模板项?
  5. iOS开发-平台使用TestFlight进行Beta测试
  6. HTML 表格中的行合并与列合并
  7. ASP.NET 缓存技术分析
  8. Java数字、货币格式化
  9. node --- 模拟express实现一个简单的服务器
  10. ES6-18/19 异步的开端-promise