理解C++对象内存布局

#include <iostream>
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 b
type = 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 <iostream>
#include "type_info.h"
#include <string>
#include <cstdlib>
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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值