zoukankan      html  css  js  c++  java
  • C++学习笔记----4.5 C++继承时的对象内存模型

    推荐阅读:http://blog.csdn.net/randyjiawenjie/article/details/6693337

              最近研究了一下,C++继承的内存对象模型。主要是读了读http://blog.csdn.net/haoel/article/details/3081328C++ 对象的内存布局)。很推荐这篇文章。

             对这篇文章做了做总结。本文的大部分内容来自于这篇文章中的总结http://blog.csdn.net/haoel/article/details/3081328(C++ 对象的内存布局

    #类中的元素

    0. 成员变量   1. 成员函数   2. 静态成员变量   3. 静态成员函数   4. 虚函数   5. 纯虚函数

    #影响对象大小的因素

    0. 成员变量     1. 虚函数表指针(_vftptr)   2. 虚基类表指针(_vbtptr)   3. 内存对齐

    _vftptr、_vbtptr的初始化由对象的构造函数, 赋值运算符自动完成;对象生命周期结束后,由对象的析构函数来销毁。
    对象所关联的类型(type_info),通常放在virtual table的第一个slot中。

    虚继承:在继承定义中包含了virtual关键字的继承关系;
    虚基类:在虚继承体系中的通过virtual继承而来的基类,需要注意的是:
    class CDerive : public virtual CBase {}; 其中CBase称之为CDerive的虚基类,而不是说CBase就是个虚基类,因为CBase还可以为不是虚继承体系中的基类。

    虚函数被派生后,仍然为虚函数,即使在派生类中省去virtual关键字。

    虚基类的构造与析构是由最终子类负责调用的(而不是直接派生子类)

    注:【下文中_vbptr即_vbtptr】

    #对象内存布局分类讨论

    vc6变量查看器中(Locals,Watch1等),也可以看到部分对象布局的情况(不完整,且虚继承是错误的)。

    vs2005及以后版本的编译器提供了/d1reportSingleClassLayout[类名]编译选项来查看对象完整的内存布局:

    cl classLayout.cpp /d1reportSingleClassLayoutCChildren

    注:下文举例的类图中函数均为虚函数(斜体 表示该函数为虚函数)

    0. 单一类

    (1). 空类

    sizeof(CNull)=1(用于标识该对象)

    (2). 只有成员变量的类

    int nVarSize = sizeof(CVariable) = 12

     

    内存布局:

    (3). 只有虚函数的类

    int nVFuntionSize = sizeof(CVFuction) = 4(虚表指针)

     

    内存布局:

    (4). 有成员变量、虚函数的类

     

    int nParentSize = sizeof(CParent) = 8

    内存布局:

    1. 单一继承(含成员变量、虚函数、虚函数覆盖)

    int nChildSize = sizeof(CChildren) = 12

    vc中显示的结果(注:还有1个虚函数CChildren::g1没有被显示出来):

    d1reportSingleClass查看:

    内存布局:

    2. 多继承 (含成员变量、虚函数、虚函数覆盖)

    int nChildSize = sizeof(CChildren) = 20

    vc中显示的结果(注:还有2个虚函数CChildren::f2,CChildren::h2没有被显示出来,this指针的adjustor[调整值]也没打印出):

    d1reportSingleClass查看:

    内存布局:

    3. 深度为2的继承(含成员变量、虚函数、虚函数覆盖)

     

    int nGrandSize = sizeof(CGrandChildren) = 24

    vc中显示的结果(注:还有3个虚函数CGrandChildren::f2,CChildren::h2,CGrandChildren::f3没有显示出来,this指针的adjustor[调整值]也没打印出):

    d1reportSingleClass查看:

    内存布局:

    4 重复继承(含成员变量、虚函数、虚函数覆盖)

     

    int nGrandSize = sizeof(CGrandChildren) = 28

    vc中显示的结果(注:还有大量的虚函数没有显示出来,this指针的adjustor[调整值]也没打印出):

    thunk函数:一种形实转换辅助函数;主要做this指针调整,函数调用重定向。

    d1reportSingleClass查看:

    内存布局:

    由于m_nAge在内容中存在两个拷贝,因此我们不能直接通过pGrandChildrenA->m_nAge来访问该变量,

    这样会存在二义性,编译器无法知道应该访问CChildren1中的m_nAge,还是CChildren2中的m_nAge。

    为了标识唯一的m_nAge,就需要带上其所在范围的类名了。如下:

    1 pGrandChildrenA->CChildren1::m_nAge = 1;
    2 pGrandChildrenA->CChildren2::m_nAge = 2;

    5. 单一虚继承(含成员变量、虚函数、虚函数覆盖)

     

    int nChildSize = sizeof(CChildren) = 20

    d1reportSingleClass查看:

    内存布局:

     

    6. 多虚继承(含成员变量、虚函数、虚函数覆盖)

    (1) virtual CParent1, CParent2

    int nChildSize = sizeof(CChildren) = 24

    d1reportSingleClass查看:

    内存布局:

    (2) CParent1, virtual CParent2

    int nChildSize = sizeof(CChildren) = 24

    d1reportSingleClass查看:

    内存布局:

    (3) virtual CParent1, virtual CParent2

    int nChildSize = sizeof(CChildren) = 28

    d1reportSingleClass查看:

    内存布局:

    7. 钻石型的虚拟多重继承(含成员变量、虚函数、虚函数覆盖)

    int nGrandChildSize = sizeof(CGrandChildren) = 36

    d1reportSingleClass查看:

    thunk函数:一种形实转换辅助函数;主要做this指针调整,函数调用重定向。

    内存布局:

  • 相关阅读:
    java的sha1加密,转化为python版本
    VUE:页面跳转时传递参数,及参数获取
    如何使用 Django中的 get_queryset, get_context_data和 get_object 等方法
    django orm 外键id返回对应的名称
    spring boot(一):入门篇
    redis学习(四)——Hash数据类型
    redis学习(三)——List数据类型
    redis学习(二)——String数据类型
    Java多线程(七)——线程休眠
    MySQL和B树的那些事
  • 原文地址:https://www.cnblogs.com/haoyul/p/7287719.html
Copyright © 2011-2022 走看看