C++ 对象的内存布局(下)

C++内存布局(下) 上一篇已经分析了基本结构体、C++简单对象和加上一般的继承之后的各种情况,这篇文章将主要在有虚继承时,C++对象的内存又是怎样分配的。虚继承虚继承解决了从不同途径继承的类具有共同基类的问题,使得共同基类只有一份拷贝。解决了二义性的问题,也节省了内存。虚继承的一般类图如下所示:sizeof(Derived) = 40 字节。经过分析,可以得到Derived类在内存中的存储结构示意图为:总结: 各部分在 阅读详情

C++ 对象的内存布局(下)

 

陈皓

http://blog.youkuaiyun.com/haoel

 

 


 

重复继承

 

下面我们再来看看,发生重复继承的情况。所谓重复继承,也就是某个基类被间接地重复继承了多次。

 

下图是一个继承图,我们重载了父类的f()函数。

 


 

其类继承的源代码如下所示。其中,每个类都有两个变量,一个是整形(4字节),一个是字符(1字节),而且还有自己的虚函数,自己overwrite父类的虚函数。如子类D中,f()覆盖了超类的函数, f1() 和f2() 覆盖了其父类的虚函数,Df()为自己的虚函数。

 

class B

{

    public:

        int ib;

        char cb;

    public:

        B():ib(0),cb('B') {}

 

        virtual void f() { cout << "B::f()" << endl;}

        virtual void Bf() { cout << "B::Bf()" << endl;}

};

class B1 :  public B

{

    public:

        int ib1;

        char cb1;

    public:

        B1():ib1(11),cb1('1') {}

 

        virtual void f() { cout << "B1::f()" << endl;}

        virtual void f1() { cout << "B1::f1()" << endl;}

        virtual void Bf1() { cout << "B1::Bf1()" << endl;}

 

};

class B2:  public B

{

    public:

        int ib2;

        char cb2;

    public:

        B2():ib2(12),cb2('2') {}

 

        virtual void f() { cout << "B2::f()" << endl;}

        virtual void f2() { cout << "B2::f2()" << endl;}

        virtual void Bf2() { cout << "B2::Bf2()" << endl;}

       

};

 

class D : public B1, public B2

{

    public:

        int id;

        char cd;

    public:

        D():id(100),cd('D') {}

 

        virtual void f() { cout << "D::f()" << endl;}

        virtual void f1() { cout << "D::f1()" << endl;}

        virtual void f2() { cout << "D::f2()" << endl;}

        virtual void Df() { cout << "D::Df()" << endl;}

       

};

我们用来存取子类内存布局的代码如下所示:(在VC++ 2003和G++ 3.4.4下)

    typedef void(*Fun)(void);

    int** pVtab = NULL;

    Fun pFun = NULL;

 

    D d;

    pVtab = (int**)&d;

    cout << "[0] D::B1::_vptr->" << endl;

    pFun = (Fun)pVtab[0][0];

    cout << "     [0] ";    pFun();

    pFun = (Fun)pVtab[0][1];

    cout << "     [1] ";    pFun();

    pFun = (Fun)pVtab[0][2];

    cout << "     [2] ";    pFun();

    pFun = (Fun)pVtab[0][3];

    cout << "     [3] ";    pFun();

    pFun = (Fun)pVtab[0][4];

    cout << "     [4] ";    pFun();

    pFun = (Fun)pVtab[0][5];

    cout << "     [5] 0x" << pFun << endl;

   

    cout << "[1] B::ib = " << (int)pVtab[1] << endl;

    cout << "[2] B::cb = " << (char)pVtab[2] << endl;

    cout << "[3] B1::ib1 = " << (int)pVtab[3] << endl;

    cout << "[4] B1::cb1 = " << (char)pVtab[4] << endl;

 

    cout << "[5] D::B2::_vptr->" << endl;

    pFun = (Fun)pVtab[5][0];

    cout << "     [0] ";    pFun();

    pFun = (Fun)pVtab[5][1];

    cout << "     [1] ";    pFun();

    pFun = (Fun)pVtab[5][2];

    cout << "     [2] ";    pFun();

    pFun = (Fun)pVtab[5][3];

    cout << "     [3] ";    pFun();

    pFun = (Fun)pVtab[5][4];

    cout << "     [4] 0x" << pFun << endl;

 

    cout << "[6] B::ib = " << (int)pVtab[6] << endl;

    cout << "[7] B::cb = " << (char)pVtab[7] << endl;   

    cout << "[8] B2::ib2 = " << (int)pVtab[8] << endl;

    cout << "[9] B2::cb2 = " << (char)pVtab[9] << endl;

 

    cout << "[10] D::id = " << (int)pVtab[10] << endl;

    cout << "[11] D::cd = " << (char)pVtab[11] << endl;

 

程序运行结果如下:

 

GCC 3.4.4

VC++ 2003

[0] D::B1::_vptr->

     [0] D::f()

     [1] B::Bf()

     [2] D::f1()

     [3] B1::Bf1()

     [4] D::f2()

     [5] 0x1

[1] B::ib = 0

[2] B::cb = B

[3] B1::ib1 = 11

[4] B1::cb1 = 1

[5] D::B2::_vptr->

     [0] D::f()

     [1] B::Bf()

     [2] D::f2()

     [3] B2::Bf2()

     [4] 0x0

[6] B::ib = 0

[7] B::cb = B

[8] B2::ib2 = 12

[9] B2::cb2 = 2

[10] D::id = 100

[11] D::cd = D

[0] D::B1::_vptr->

     [0] D::f()

     [1] B::Bf()

     [2] D::f1()

     [3] B1::Bf1()

     [4] D::Df()

     [5] 0x00000000

[1] B::ib = 0

[2] B::cb = B

[3] B1::ib1 = 11

[4] B1::cb1 = 1

[5] D::B2::_vptr->

     [0] D::f()

     [1] B::Bf()

     [2] D::f2()

     [3] B2::Bf2()

     [4] 0x00000000

[6] B::ib = 0

[7] B::cb = B

[8] B2::ib2 = 12

[9] B2::cb2 = 2

[10] D::id = 100

[11] D::cd = D

 

下面是对于子类实例中的虚函数表的图:

 

 

 

我们可以看见,最顶端的父类B其成员变量存在于B1和B2中,并被D给继承下去了。而在D中,其有B1和B2的实例,于是B的成员在D的实例中存在两份,一份是B1继承而来的,另一份是B2继承而来的。所以,如果我们使用以下语句,则会产生二义性编译错误:

 

D d;

d.ib = 0;               //二义性错误

d.B1::ib = 1;           //正确

d.B2::ib = 2;           //正确


注意,上面例程中的最后两条语句存取的是两个变量。虽然我们消除了二义性的编译错误,但B类在D中还是有两个实例,这种继承造成了数据的重复,我们叫这种继承为重复继承。重复的基类数据成员可能并不是我们想要的。所以,C++引入了虚基类的概念。

 

 

钻石型多重虚拟继承

 

虚拟继承的出现就是为了解决重复继承中多个间接父类的问题的。钻石型的结构是其最经典的结构。也是我们在这里要讨论的结构:

 

上述的“重复继承”只需要把B1和B2继承B的语法中加上virtual 关键,就成了虚拟继承,其继承图如下所示:

 

 


 

上图和前面的“重复继承”中的类的内部数据和接口都是完全一样的,只是我们采用了虚拟继承:其省略后的源码如下所示:

 

class B {……};

class B1 : virtual public B{……};

class B2: virtual public B{……};

class D : public B1, public B2{ …… };

 

在查看D之前,我们先看一看单一虚拟继承的情况。下面是一段在VC++2003下的测试程序:(因为VC++和GCC的内存而局上有一些细节上的不同,所以这里只给出VC++的程序,GCC下的程序大家可以根据我给出的程序自己仿照着写一个去试一试):

 

    int** pVtab = NULL;

    Fun pFun = NULL;

 

    B1 bb1;

 

    pVtab = (int**)&bb1;

    cout << "[0] B1::_vptr->" << endl;

    pFun = (Fun)pVtab[0][0];

    cout << "     [0] ";

    pFun(); //B1::f1();

    cout << "     [1] ";

    pFun = (Fun)pVtab[0][1];

    pFun(); //B1::bf1();

    cout << "     [2] ";

    cout << pVtab[0][2] << endl;

 

    cout << "[1] = 0x";

    cout << (int*)*((int*)(&bb1)+1) <<endl; //B1::ib1

    cout << "[2] B1::ib1 = ";

    cout << (int)*((int*)(&bb1)+2) <<endl; //B1::ib1

    cout << "[3] B1::cb1 = ";

    cout << (char)*((int*)(&bb1)+3) << endl; //B1::cb1

 

    cout << "[4] = 0x";

    cout << (int*)*((int*)(&bb1)+4) << endl; //NULL

 

    cout << "[5] B::_vptr->" << endl;

    pFun = (Fun)pVtab[5][0];

    cout << "     [0] ";

    pFun(); //B1::f();

    pFun = (Fun)pVtab[5][1];

    cout << "     [1] ";

    pFun(); //B::Bf();

    cout << "     [2] ";

    cout << "0x" << (Fun)pVtab[5][2] << endl;

 

    cout << "[6] B::ib = ";

    cout << (int)*((int*)(&bb1)+6) <<endl; //B::ib

    cout << "[7] B::cb = ";

 

其运行结果如下(我结出了GCC的和VC++2003的对比):

 

GCC 3.4.4

VC++ 2003

[0] B1::_vptr ->

    [0] : B1::f()

    [1] : B1::f1()

    [2] : B1::Bf1()

    [3] : 0

[1] B1::ib1 : 11

[2] B1::cb1 : 1

[3] B::_vptr ->

    [0] : B1::f()

    [1] : B::Bf()

    [2] : 0

[4] B::ib : 0

[5] B::cb : B

[6] NULL : 0

[0] B1::_vptr->

     [0] B1::f1()

     [1] B1::Bf1()

     [2] 0

[1] = 0x00454310 ç该地址取值后是-4

[2] B1::ib1 = 11

[3] B1::cb1 = 1

[4] = 0x00000000

[5] B::_vptr->

     [0] B1::f()

     [1] B::Bf()

     [2] 0x00000000

[6] B::ib = 0

[7] B::cb = B

 

 

这里,大家可以自己对比一下。关于细节上,我会在后面一并再说。

 

下面的测试程序是看子类D的内存布局,同样是VC++ 2003的(因为VC++和GCC的内存布局上有一些细节上的不同,而VC++的相对要清楚很多,所以这里只给出VC++的程序,GCC下的程序大家可以根据我给出的程序自己仿照着写一个去试一试):

 

    D d;

 

    pVtab = (int**)&d;

    cout << "[0] D::B1::_vptr->" << endl;

    pFun = (Fun)pVtab[0][0];

    cout << "     [0] ";    pFun(); //D::f1();

    pFun = (Fun)pVtab[0][1];

    cout << "     [1] ";    pFun(); //B1::Bf1();

    pFun = (Fun)pVtab[0][2];

    cout << "     [2] ";    pFun(); //D::Df();

    pFun = (Fun)pVtab[0][3];

    cout << "     [3] ";

    cout << pFun << endl;

 

    //cout << pVtab[4][2] << endl;

    cout << "[1] = 0x";

    cout <<  (int*)((&dd)+1) <<endl; //????

 

    cout << "[2] B1::ib1 = ";

    cout << *((int*)(&dd)+2) <<endl; //B1::ib1

    cout << "[3] B1::cb1 = ";

    cout << (char)*((int*)(&dd)+3) << endl; //B1::cb1

 

    //---------------------

    cout << "[4] D::B2::_vptr->" << endl;

    pFun = (Fun)pVtab[4][0];

    cout << "     [0] ";    pFun(); //D::f2();

    pFun = (Fun)pVtab[4][1];

    cout << "     [1] ";    pFun(); //B2::Bf2();

    pFun = (Fun)pVtab[4][2];

    cout << "     [2] ";

    cout << pFun << endl;

   

    cout << "[5] = 0x";

    cout << *((int*)(&dd)+5) << endl; // ???

 

    cout << "[6] B2::ib2 = ";

    cout << (int)*((int*)(&dd)+6) <<endl; //B2::ib2

    cout << "[7] B2::cb2 = ";

    cout << (char)*((int*)(&dd)+7) << endl; //B2::cb2

 

    cout << "[8] D::id = ";

    cout << *((int*)(&dd)+8) << endl; //D::id

    cout << "[9] D::cd = ";

    cout << (char)*((int*)(&dd)+9) << endl;//D::cd

 

    cout << "[10]  = 0x";

    cout << (int*)*((int*)(&dd)+10) << endl;

    //---------------------

    cout << "[11] D::B::_vptr->" << endl;

    pFun = (Fun)pVtab[11][0];

    cout << "     [0] ";    pFun(); //D::f();

    pFun = (Fun)pVtab[11][1];

    cout << "     [1] ";    pFun(); //B::Bf();

    pFun = (Fun)pVtab[11][2];

    cout << "     [2] ";

    cout << pFun << endl;

 

    cout << "[12] B::ib = ";

    cout << *((int*)(&dd)+12) << endl; //B::ib

    cout << "[13] B::cb = ";

    cout << (char)*((int*)(&dd)+13) <<endl;//B::cb

 

下面给出运行后的结果(分VC++和GCC两部份)

 

 

GCC 3.4.4

VC++ 2003

[0] B1::_vptr ->

    [0] : D::f()

    [1] : D::f1()

    [2] : B1::Bf1()

    [3] : D::f2()

    [4] : D::Df()

    [5] : 1

[1] B1::ib1 : 11

[2] B1::cb1 : 1

[3] B2::_vptr ->

    [0] : D::f()

    [1] : D::f2()

    [2] : B2::Bf2()

    [3] : 0

[4] B2::ib2 : 12

[5] B2::cb2 : 2

[6] D::id : 100

[7] D::cd : D

[8] B::_vptr ->

    [0] : D::f()

    [1] : B::Bf()

    [2] : 0

[9] B::ib : 0

[10] B::cb : B

[11] NULL : 0

[0] D::B1::_vptr->

     [0] D::f1()

     [1] B1::Bf1()

     [2] D::Df()

     [3] 00000000

[1] = 0x0013FDC4  ç 该地址取值后是-4

[2] B1::ib1 = 11

[3] B1::cb1 = 1

[4] D::B2::_vptr->

     [0] D::f2()

     [1] B2::Bf2()

     [2] 00000000

[5] = 0x4539260   ç 该地址取值后是-4

[6] B2::ib2 = 12

[7] B2::cb2 = 2

[8] D::id = 100

[9] D::cd = D

[10]  = 0x00000000

[11] D::B::_vptr->

     [0] D::f()

     [1] B::Bf()

     [2] 00000000

[12] B::ib = 0

[13] B::cb = B

 

 

关于虚拟继承的运行结果我就不画图了(前面的作图已经让我产生了很严重的厌倦感,所以就偷个懒了,大家见谅了)

 

在上面的输出结果中,我用不同的颜色做了一些标明。我们可以看到如下的几点:

 

1)无论是GCC还是VC++,除了一些细节上的不同,其大体上的对象布局是一样的。也就是说,先是B1(黄色),然后是B2(绿色),接着是D(灰色),而B这个超类(青蓝色)的实例都放在最后的位置。

2)关于虚函数表,尤其是第一个虚表,GCC和VC++有很重大的不一样。但仔细看下来,还是VC++的虚表比较清晰和有逻辑性。

3)VC++和GCC都把B这个超类放到了最后,而VC++有一个NULL分隔符把B和B1和B2的布局分开。GCC则没有。

4)VC++中的内存布局有两个地址我有些不是很明白,在其中我用红色标出了。取其内容是-4。接道理来说,这个指针应该是指向B类实例的内存地址(这个做法就是为了保证重复的父类只有一个实例的技术)。但取值后却不是。这点我目前还并不太清楚,还向大家请教。

5)GCC的内存布局中在B1和B2中则没有指向B的指针。这点可以理解,编译器可以通过计算B1和B2的size而得出B的偏移量。

 

 

结束语

C++这门语言是一门比较复杂的语言,对于程序员来说,我们似乎永远摸不清楚这门语言背着我们在干了什么。需要熟悉这门语言,我们就必需要了解C++里面的那些东西,需要我们去了解他后面的内存对象。这样我们才能真正的了解C++,从而能够更好的使用C++这门最难的编程语言。


C++继承内存对象模型 最近研究了一下,C++继承的内存对象模型。主要是读了读http://blog.youkuaiyun.com/haoel/article/details/3081328(C++ 对象的内存布局)。很推荐这篇文章。          对这篇文章做了做总结。本文的大部分内容来自于这篇文章中的总结 阅读详情

相关推荐

C++对象模型之简述C++对象的内存布局

在C++中,有两种类的成员变量:static和非static,有三种成员函数:static、非static和virtual。那么,它们如何影响C++的对象在内存中的分布呢? 当存在继承的情况下,其内存分布又是如何呢? 下面就一个非常简单的类,通过逐渐向其中加入各种成员,来逐一分析上述两种成员变量及三种成员函数对类的对象的内存分布的影响。 注:以下的代码的测试结果均是基于Ubuntu 14.0

ljianhui的专栏 1万+

C++语法——详解虚继承

一.什么是虚继承所谓虚继承(virtual)就是子类中只有一份间接父类的数据。该技术用于解决多继承中的父类为非虚基类时出现的数据冗余问题,即菱形继承问题。,那么,当我们用D实例化对象调用a时,编译会报错,因为发生了混淆。除非指定类域B或C。当然指定A也可以,因为默认会从第一个父类中找。此时,D的实例化对象内部结构如下:而当我们使用虚继承时,结构是下图这样,D中只有一份父类A,当我们调用A中数据时,并不会发生冗余。

weixin_61857742的博客 3万+

UVC协议在STM32H7上的移植实战案例

手把手带你把UVC协议移植到STM32H7平台,重点解决USB高速枚举、视频流控制与YUV数据打包等关键问题。基于HAL库实现符合UVC 1.5规范的即插即用摄像头功能,让嵌入式设备轻松变身标准UVC视频源。

weixin_42437253的博客 397

C++ 对象的内存布局(下)(转)

C++ 对象的内存布局(下) 陈皓http://blog.youkuaiyun.com/haoel  <<<点击这里查看上篇  重复继承 下面我们再来看看,发生重复继承的情况。所谓重复继承,也就是某个基类被间接地重复继承了多次。 下图是一个继承图,我们重载了父类的f()函数。   其类继承的源代码如下所示。其中,每个类都有两个变量,一个是整形(4字节

gxj1680的专栏 693

别再卡在Google验证了!用Header Editor插件5分钟搞定TryHackMe注册(附最新.json规则)

本文提供了使用Header Editor插件快速解决TryHackMe注册时Google验证失败问题的详细指南。通过导入特定的JSON规则文件,用户可以轻松绕过网络限制,5分钟内完成账号注册,无需复杂设置。文章包含完整的配置步骤、常见问题解决方案以及安全使用建议。

weixin_30736301的博客 463

Windows下的Geant4安装教程

本教程将从0开始展示在Windows下如何安装Geant4,包括各种辅助工具(QT、Cmake、Visual Studio)的下载安装,环境变量的配置,以及example的生成等。

weixin_49065278的博客 4107

C++ 对象内存模型

继承,封装,多态是面向对象的三大特性之一,以类为例,类分为数据成员和函数 而函数分为static、非static、virtual函数三大类函数,那么一般的C++对象其内存模型是如何。 类中存在部分: 成员变量 静态成员变量 成员函数 静态成员函数 构造函数 析构函数 虚函数 虚析构函数 纯虚函数 通常而言对于一个对象,其在内存中的模型不包含函数,只包含成员,而函数单独存放,所有对象通过地址进行调用。对象在内存模型及其占用大小受几方面因素影响: 成员变量 虚表指针 虚基类表指.

一只老风铃 1847

Simulink/Requirements Toolbox 学习(一):概述

编写、导入、导出需求并使需求正式化;两种创建需求的模块:需求编辑器、需求透视;描述需求的方式:文本、图像和关键字;通过导入和导出需求文件,与第三方需求管理工具(例如 Microsoft Word、Microsoft Excel®、IBM DOORS和 IBM DOORS Next)集成。涉及模块:Requirements Eidtor、Requirements Table等;

855

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

C++ 对象的内存布局(上) 陈皓http://blog.youkuaiyun.com/haoel  点击这里查看下篇>>>前言 07年12月,我写了一篇《C++虚函数表解析》的文章,引起了大家的兴趣。有很多朋友对我的文章留了言,有鼓励我的,有批评我的,还有很多问问题的。我在这里一并对大家的留言表示感谢。这也是我为什么再写一篇续言的原因。因为,在上一篇文章中,我用了的示例都是非

陈皓专栏 【空谷幽兰,心如皓月】 17万+

保姆级教程:用Simulink从零搭建新能源汽车VCU蠕行控制模型(附模型下载)

本文提供了一份详细的Simulink建模教程,指导读者从零开始搭建新能源汽车VCU蠕行控制模型。内容涵盖环境配置、信号定义、蠕行模式使能条件、扭矩控制算法实现及模型验证等关键步骤,特别适合需要将控制算法转化为可执行模型的初学者。教程还包含实用代码示例和常见问题解决方案,帮助开发者快速掌握VCU蠕行控制的Simulink实现技巧。

547

【深度学习基础】多任务学习(Multi-Task Learning)

多任务学习(Multi-task learning)是和单任务学习(single-task learning)相对的一种机器学习方法。在机器学习领域,标准的算法理论是一次学习一个任务,也就是系统的输出为实数的情况。复杂的学习问题先被分解成理论上独立的子问题,然后分别对每个子问题进行学习,最后通过对子问题学习结果的组合建立复杂问题的数学模型。多任务学习是一种联合学习,多个任务并行学习,结果相互影响。一次只学习一个任务(task),大部分的机器学习任务都属于单任务学习。

十二月的猫 1万+

C++对象的内存布局

本文介绍一个C++ struct/class在内存中的布局是什么样的,包括数据成员和方法成员,静态成员,非静态成员,虚拟成员在内存中的位置;本文不涉及(多)继承情况下对象内存布局的细节。

giantpoplar的专栏 4172

SAP_SD模块更改销售订单定价简介

方式优点缺点适用场景MASS简单,无需开发不能触发重新定价逻辑修改特定字段BDC (VA02)可模拟人工操作维护复杂,速度慢少量订单批改稳定、安全需ABAP支持推荐用于批量重新定价VBOF标准重算返利仅适返利类条件返利更新通过本文,我们了解了 SAP 的[SAP_SD模块更改销售订单定价]的基本概念和应用方法。希望这些信息能帮助您在实际工作中更好地使用 SAP 系统,提高工作效率和管理水平。更多SAP文章请点击更多SAP文章更新,大家一起学习进步!!!

我小时候很黑的博客 713

C++ 对象内存布局

可能会对 C++ 对象的内存布局产生影响的因素: 对象的数据成员变量 对象的一般成员函数 对象的虚成员函数 对象继承 - 父类不包含虚函数 对象继承 - 父类包含虚函数 对象多继承 - 父类不包含虚函数 对象多继承 - 父类包含虚函数 对象虚继承 动态类型信息(这里不讨论) 通过汇编代码看虚函数调用过程 这些特性的实现常常因操作系统平台和编译器/ABI 而异,这里的分析主要基于 linux x86_64 平台上的 GCC 11.2.0/G++ 11.2.0 的行为进行。 一般 C++ 对象的内存布局

tq08g2z的专栏 1458

OpenClaw赚钱实录:从“养龙虾“到可持续变现的实践指南——OpenClaw的赚钱底层逻辑与“记忆”革命

AI 的商业价值不在于替代思考,而在于沉淀关系。当 Agent 记住你的偏好、你的历史、你的目标,它就不再是冷冰冰的软件,而是成为数字化的关系资产。这种资产的稀缺性不在于技术复杂度,而在于时间无法被压缩——你无法用更好的算法替代 3 个月的协作记忆。在这场“记忆革命”中,护城河不是模型参数,而是用户与 AI 共同书写的历史。OpenClaw 用一套优雅的文件系统架构,将这种历史转化为可积累、可复用、可变现的数字资产。从“计算即服务”(CaaS)到“记忆即服务”(MaaS)。

冯·诺依曼 1341

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

C++类对象在内存中的布局

目录 一、前言 二、C++ 类对象的内存布局 2.1 只有数据成员的对象 2.2 没有虚函数的对象 2.3 拥有仅一个虚函数的类对象 2.4 拥有多个虚函数的类对象 三、继承关系中的C++类对象内存分布 3.1 存在继承关系且本身不存在虚函数的派生类的内存布局 ​3.2本身不存在虚函数(不严谨)但存在基类虚函数覆盖的单继承类的内存布局 3.3 定义了基类没有的虚函数的单继承的类对象布局 3.4 多继承且存在虚函数覆盖同时又存在自身定义的虚函数的类对象布局 3.5如果第1个直接基...

qq_41687938的博客 3601

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

C++ 对象的内存布局

C++ 对象的内存布局是一个重要的概念,它影响着对象的存储、访问效率以及内存使用。理解对象的内存布局有助于优化程序性能和避免潜在的内存问题。以下是 C++ 对象内存布局的主要组成部分:数据成员是对象的状态表示,通常是类中定义的变量。数据成员的存储顺序通常是按照它们在类中声明的顺序排列。需要注意的是,编译器可能会在数据成员之间插入填充(padding),以满足对齐要求。对齐是指数据在内存中存储时的地址要求。不同类型的数据可能有不同的对齐要求。例如, 通常需要在 4 字节对齐的地址上存储,而 可能需要在 8

本博客聚焦游戏开发技巧、创业实战心得及大学热门课程干货。这里既有技术深度,也有思维广度,无论你是开发者、创业者还是高校学子,都能在这里找到适合自己的成长之路! 1057

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

C++类对象的内存布局

1、C++类对象的内存布局 在C++的类对象中,有两种类的成员变量:static和非static,有三种成员函数:static、非static和virtual。那么,它们在C++的内存中是如何分布的呢? C++程序的内存格局通常分为四个区:全局数据区(data area),代码区(code area),栈区(stack area),堆区(heap area)(即自由存储区...

好记性不如写博客! 8995

C++对象内存布局探究

C++对象内存布局探究。

xuyouqiang1987的博客 1112

C++类的对象内存布局

1. **类对象的内存布局**1.1 **单一类对象的内存布局**1.2 **带有虚函数的类对象**2. **类的嵌套结构的内存布局**2.1 **简单嵌套类**3. **继承类对象的内存布局**3.1 **没有虚函数的继承**3.2 **有虚函数的继承**4. **智能指针管理的类对象的内存布局**5. **总结**欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格。

_程序猿 1235

C++对象模型之详述C++对象的内存布局

本文主要讨论继承对于对象的内存分布的影响,包括:继承后类的对象的成员的布局、继承对于虚函数表的影响、virtual函数机制如何实现、运行时类型识别等。由于在C++中继承的关系比较复杂,所以本文会讨论如下的继承情况: 1)单一继承 2)多重继承 3)重复继承 4)单一虚拟继承 5)钻石型虚拟继承

ljianhui的专栏 1万+

C++对象内存布局

http://blog.youkuaiyun.com/lights_joy/archive/2009/09/09/4534660.aspxC++对象内存布局(1)... 11.1    最简单的类... 11.1.1   赋值语句的行为... 21.1.2   函数调用的行为... 21.1.3   当p指向自定义的缓冲区... 3C++对象内存布局(2)... 41.1.1  

JonsenElizee 1069

c++对象内存布局

在c++中,有两种数据成员:static和nonstatic,三种成员函数:static、nonstatic和virtual。c++对象模型:nonstatic data members被置于类对象之内,而staticdata members被置于类对象之外。static和nonstaticfunction members被放在类对象之外。virtual函数则由如下步骤支持: 1)每个cla

ruan875417的专栏 1281
上一篇: C++ 对象的内存布局(上)
下一篇: C++虚拟继承问题
Superr爬爬虫
博客等级 码龄19年 72粉丝 93原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值