C++对象模型

1、简单模型

1.1、概述

C++类中有两种数据成员:static、nonstatic;三种成员函数:static、nonstatic、virtual。它们在内存中的布局方式和访问方式是不同的。像nonstatic对象是在类对象内存储,虚函数是通过一个虚表指针指向一个虚表再通过虚表查找。静态和非静态函数都在对象之外存储。

1.2、对象模型图例

定义一个C++类:
这里写图片描述

class Base
{
public:
    Base(int);
    virtual ~Base(void);
    virtual void base_print() const;

    static void baseStaticFunc();
    void baseNonStaticFunc();

protected:
    int iBase;
    static int iSatic;
};

Base类中各种成员如下图方式存储、访问:
这里写图片描述
不同编译存储各个成员可能有所差异,这是在Windows+VS2012中的布局。
可以看出Base对象中第一项是虚表指针,这个指针指向一张虚表,虚表中存储的是Base类各个虚函数的存储地址。在紧邻虚表的前一个位置是一个typeinfoPtr指针,它指向的是Base类的type_info信息。Base类的静态和非静态成员函数,静态成员数据在Base对象外存储。

1.3、验证模型

以下代码验证1.2图中模型:

void Base_Model()
{
    cout << "********************Base b Model********************" << endl;
    Base b(3);
    cout << "对象b的内存地址 &b = " << &b << endl;
    cout << "对象b中第一项 _vptr Base 虚表指针的值也即虚表首地址 = " << hex << *(int*)(&b) << endl;
    cout << "对象b中第二项 int Base::iBase的值 = " << hex << *((int*)(&b) + 1) << endl;

    cout << "虚函数表中第一个slot的值也即虚函数Base::~Base()的入口地址 = " << hex << *(int*)(*(int*)(&b)) << endl;
    cout << "虚函数表中第二个slot的值也即虚函数Base::base_print()的入口地址 = " << hex << *((int*)(*(int*)(&b)) + 1) << endl;

    pFun pBasePrint = (pFun)(*((int*)(*(int*)(&b)) + 1));       
    pBasePrint();   //注意这里输出是不一样的,这个调用仅仅是调用那段代码但是里面的iBase值不确定,因为iBase在Base对象中
    b.base_print();

    cout << "typeinfoPtr指针的存储位置 = " << ((*(int*)(&b)) - 1) << endl;
    cout << "typeinfoPtr指针的值也即Base's type_info的首地址 = " << hex << *((int*)(*(int*)(&b)) - 1) << endl;

//  cout << hex << Base::baseStaticFunc << endl;
//  cout << hex << Base::baseStaticFunc2 << endl;
//  cout << hex << &Base::baseNonStaticFunc << endl;
//  typedef void (Base::*pmf)();
//  pmf pt = &Base::baseNonStaticFunc;
//  cout << hex << pt << endl;
}

运行结果:
这里写图片描述

2、对象模型with单继承

2.1、无重写单继承

无重写指的是派生类没有重写基类的虚函数。定义一个Base的派生类(除析构函数外无重写):
这里写图片描述

class Derived : public Base
{
public:
    Derived(int);
    virtual ~Derived(void);
    virtual void derived_print(void);

protected:
    int iDerived;
};

则派生类Derived及对象的布局方式如下图:
这里写图片描述
可以看到父类的虚函数放在了虚函数表的前面,后面放的是子类新加入的虚函数,子类的虚析构函数覆盖了父类的虚析构函数。
验证模型:

void DerivedInheritBase()
{
    cout << "********************Derived d Model********************" << endl;
    Derived d(6);
    cout << "对象d的内存地址 &d = " << &d << endl;
    cout << "对象d中第一项 _vptr Derived 虚表指针的值也即虚表首地址 = " << hex << *(int*)(&d) << endl;
    cout << "对象d中第二项 int Base::iBase的值 = " << hex << *((int*)(&d) + 1) << endl;
    cout << "对象d中第三项 int Derived::iDerived的值 = " << hex << *((int*)(&d) + 2) << endl;

    cout << "虚函数表中第一个slot的值也即虚函数Derived::~Derived()的入口地址 = " << hex << *(int*)(*(int*)(&d)) << endl;
    cout << "虚函数表中第二个slot的值也即虚函数Base::base_print()的入口地址 = " << hex << *((int*)(*(int*)(&d)) + 1) << endl;
    cout << "虚函数表中第二个slot的值也即虚函数Derived::derived_print()的入口地址 = " << hex << *((int*)(*(int*)(&d)) + 2) << endl;

    pFun pBasePrint = (pFun)(*((int*)(*(int*)(&d)) + 1));       
    pBasePrint();   
    d.base_print();
    pFun pDerivdPrint = (pFun)(*((int*)(*(int*)(&d)) + 2));
    pDerivdPrint();
    d.derived_print();

    cout << "typeinfoPtr指针的存储位置 = " << ((*(int*)(&d)) - 1) << endl;
    cout << "typeinfoPtr指针的值也即Derived's type_info的首地址 = " << hex &l
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值