博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
理解C++对象内存布局
阅读量:4496 次
发布时间:2019-06-08

本文共 4704 字,大约阅读时间需要 15 分钟。

#include 
using namespace std;class Base{public: Base(int mem1 = 1, int mem2 = 2) : m_iMem1(mem1), m_iMem2(mem2){ ; } virtual void vfunc1() { std::cout << "In vfunc1()" << std::endl; } virtual void vfunc2() { std::cout << "In vfunc2()" << std::endl; } virtual void vfunc3() { std::cout << "In vfunc3()" << std::endl; }private: int m_iMem1; int m_iMem2;};typedef void(*FUNC)(void);int main(int argc, char* argv[]){ Base b; // 对象b的地址 int *bAddress = (int *)&b; cout << sizeof(Base) << endl; // 对象b的vtptr的值 int *vtptr = (int *)*(bAddress + 0); printf("vtptr: 0x%08x\n", vtptr); // 对象b的第一个虚函数的地址 int *pFunc1 = (int *)*(vtptr + 0); int *pFunc2 = (int *)*(vtptr + 1); int *pFunc3 = (int *)*(vtptr + 2); cout << pFunc1 << endl; cout << pFunc2 << endl; cout << pFunc3 << endl; // 对象b的两个成员变量的值(用这种方式可轻松突破private不能访问的限制) int mem1 = (int)*(bAddress + 1); int mem2 = (int)*(bAddress + 2); printf("m_iMem1: %d \nm_iMem2: %d \n\n",mem1, mem2); // 调用虚函数 (FUNC(pFunc1))(); (FUNC(pFunc2))(); (FUNC(pFunc3))(); return 0;}

  g++ -g -std=c++11 -m32 Test3.cpp -o Test3

注意这里是编的32位程序,如果编64位程序需要修改虚函数指针的偏移以及mem1和mem2的偏移

(gdb) ptype btype = class Base {  private:    int m_iMem1;    int m_iMem2;  public:    Base(int, int);    virtual void vfunc1(void);    virtual void vfunc2(void);    virtual void vfunc3(void);} //取对象的地址,这个地址就是vptr,指向虚函数表首地址
(gdb) p &b$3 = (Base *) 0xffffcaa0(gdb) p *0xffffcaa0

  这是指向第一个虚函数的指针:

(gdb) p /x *0xffffcaa0

$5 = 0x8048b48

第一个虚函数的地址:

(gdb) p /x *0x8048b48

$7 = 0x80489d4
(gdb) p &b.vfunc1
$8 = (void (*)(Base * const)) 0x80489d4 <Base::vfunc1()>

可以验证,0x80489d4就是b.vfunc1

$9 = (void (*)(Base * const)) 0x8048a04 <Base::vfunc2()>

(gdb) p &b.vfunc3
$10 = (void (*)(Base * const)) 0x8048a34 <Base::vfunc3()>

每一个虚函数的偏移是0x30个字节

 

(gdb) p &b.m_iMem1

$11 = (int *) 0xffffcaa4

b的首地址是0xffffcaa0,第一个成员m_iMem1的地址是0xffffcaa4,说明对象的虚函数表指针vptr占用4个字节,32位机器

(gdb) p &b.m_iMem2

$12 = (int *) 0xffffcaa8

g++ -g -std=c++11 Test3.cpp -o Test3 编译64位程序:

16

vtptr: 0x00400d68
0x400bf4
0x400c20
0x400c4c
m_iMem1: 1
m_iMem2: 2

In vfunc1()

In vfunc2()
In vfunc3()

其中vptr占用8个字节,所以size是16

 

  

#include 
#include "type_info.h"#include
#include
using namespace std;typedef void(*Fun)(void);class Base { public: Base(int i) :baseI(i) {} virtual void print(void) { cout << "called virtual function Base::print()"; } virtual void setI() { cout <<"called virtual function Base::setI()"; } virtual ~Base() { } int getI() { return baseI; } static void countI() { } private: int baseI; static int baseS;};int Base::baseS = 1;void testBase(Base &p){ cout << "对象的内存起始地址:" << &p << endl; cout << "type_info信息:" << endl; cout << "虚函数表地址:" << (int *)(&p) << endl; //验证虚表 cout << "虚函数表第一个函数的地址:" << (int *)*((int*)(&p)) << endl; cout << "析构函数的地址:" << (int* )*(int *)*((int*)(&p)) << endl; cout << "虚函数表中,第二个虚函数即print()的地址:" << (int*)(*(int*)(&p) + 2) << endl; //通过地址调用虚函数print() Fun IsPrint=(Fun)*((int*)*(int*)(&p)); cout << endl; cout << "调用了虚函数: "; IsPrint(); cout << endl; // typedef void(*Fun)(void); Fun setI=(Fun)*((int*)*(int*)(&p) + 2); // (int*)(*(int*)(&p) + 1) cout << endl; cout<<"调用了虚函数: "; setI(); //若地址正确,则调用了Base类的虚函数print() cout << endl; //输入static函数的地址 p.countI();//先调用函数以产生一个实例 cout << "static函数countI()的地址:" <<(void*)p.countI << endl; //验证nonstatic数据成员 cout << "推测nonstatic数据成员baseI的地址:" << (int *)(&p) + 2 << endl; cout << "根据推测出的地址,输出该地址的值:" << *((int *)(&p) + 2) << endl; cout << "Base::getI():" << p.getI() << endl; }int main(){ Base b(1000); Fun vfunc = (Fun)*((int*)*(int*)(&b)); int *vptrAdress = (int*)(&b); cout << "virtual function table(vptr) pointer is: \t" << vptrAdress << endl; cout << "call Base::print(): "; vfunc(); testBase(b); return 0;}

virtual function table(vptr) pointer is: 0x7ffe3bd0dd60

call Base::print(): called virtual function Base::print()对象的内存起始地址:0x7ffe3bd0dd60
type_info信息:
虚函数表地址:0x7ffe3bd0dd60
虚函数表第一个函数的地址:0x401220
析构函数的地址:0x400ebc
虚函数表中,第二个虚函数即print()的地址:0x401222

调用了虚函数: called virtual function Base::print()

调用了虚函数: called virtual function Base::setI()

static函数countI()的地址:0x400f5f
推测nonstatic数据成员baseI的地址:0x7ffe3bd0dd68
根据推测出的地址,输出该地址的值:1000
Base::getI():1000

  

转载于:https://www.cnblogs.com/EMH899/p/10830746.html

你可能感兴趣的文章
iOS Xib布局某些控件显示或隐藏<约束的修改>
查看>>
软件工程第一次作业
查看>>
乘法逆元+模的运算规则
查看>>
.net 实现微信公众平台的主动推送信息
查看>>
线程池ThreadPool详解
查看>>
宝石TD迷宫设计器
查看>>
DOM对象和JQuery对象的区别
查看>>
vue脚手架安装笔记
查看>>
P2146 [NOI2015]软件包管理器
查看>>
像素与DPI之间的关系
查看>>
druid监控配置
查看>>
css颜色代码大全
查看>>
物理系统(二)
查看>>
css3中-moz、-ms、-webkit与盒子模型
查看>>
DataTable 整行为空时,去除空行,常用于Excel导入,转换为DataTable时出现
查看>>
网络相关面试题1
查看>>
一种让谷歌搜索引擎拒绝搜索的字符串
查看>>
实现毛玻璃效果
查看>>
[BZOJ4082][Wf2014]Surveillance[倍增]
查看>>
kill -9杀掉nginx主进程、reload失败解决办法
查看>>