C++对象内存布局2---进阶篇-----C++ 对象的内存布局(下)

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

重复继承

 

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

 

下图是一个继承图,我们重载了父类的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++这门最难的编程语言。

 

在文章束之前还是介绍一下自己吧。我从事软件研发有十个年头了,目前是软件开发技术主管,技术方面,主攻Unix/C/C++,比较喜欢网络上的技术,比如分布式计算,网格计算,P2P,Ajax等一切和互联网相关的东西。管理方面比较擅长于团队建设,技术趋势分析,项目管理。欢迎大家和我交流,我的MSN和Email是:haoel@hotmail.com 

 

 

<<<点击这里查看上篇

 

 

原帖:http://blog.youkuaiyun.com/haoel/article/details/3081385   作者:陈皓  (转载时请注明作者和出处。未经许可,请勿用于商业用途)

 

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

相关推荐

c++虚继承对象的内存布局

c++虚继承对象的内存布局               --VC7.1特别版网上关于c++对象布局的文章挺多,而且《深度探索c++对象模型》(Inside The C++ Object Model 侯捷 译)一书中也很详细地介绍。如果你一点都不了解C++对象的布局,我推荐你看看《深度探索c++对象模型》的第三章,如果你意犹未尽下面的两个系列都很不错:一是陈皓的《C++ 对象的内存布

BlueDog专栏 1万+

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

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

weixin_61857742的博客 3万+

PIDNet实战:如何在Cityscapes数据集上实现93.2 FPS的实时语义分割(附完整训练代码)

本文提供了在Cityscapes数据集上实现实时语义分割模型PIDNet的完整工程实践指南。PIDNet凭借其独特的比例-积分-微分三分支架构,在CVPR 2023上取得了速度与精度的出色平衡。文章详细介绍了从环境配置、数据预处理、模型训练到性能优化与部署的全流程,并分享了实现93.2 FPS高推理速度的关键技巧与代码。

weixin_29047919的博客 373

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

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

gxj1680的专栏 693

【超详细】基于YOLOv8的PCB缺陷检测

印刷电路板(PCB)瑕疵数据集:是一个公共的合成PCB数据集,由北京大学发布,其中包含1386张图像以及6种缺陷(缺失孔,鼠标咬伤,开路,短路,杂散,伪铜),用于检测,分类和配准任务。本文我们选取了其中适用与检测任务的693张图像,随机选择593张图像作为训练集,100张图像作为验证集。

weixin_45679938的博客 2万+

用迅为RK3588开发板DIY无人机飞控:从电路设计到PID调参全记录

本文详细记录了使用迅为RK3588开发板DIY无人机飞控的全过程,从硬件选型、PCB设计到实时系统构建和PID调参。RK3588核心板凭借其强大的AI算力和丰富接口,成为高性能无人机控制解决方案的理想选择。文章还分享了视觉导航系统集成和实际飞行测试经验,为硬件爱好者提供实用参考。

weixin_29053577的博客 426

C++ 对象内存模型

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

一只老风铃 1846

arduino esp8266_Arduino传感器教程 第22章ESP8266 控制 5V 继电器

ESP8266控制5V继电器目录一、NodeMCU二、工作原理本文将使用亚马逊 AlexaEcho 和 ESP8266 控制 5V 继电器。NodeMCUNodeMCU 是一个开源的 IoT 物联网硬件开发板,由于它支持 WIFI 功能且使用方法十分类似 Arduino 开发板,所以近些年获得了越来越多来自全世界的创客朋友们的青睐。NodeMCU 尺寸与 Arduino Nano 类似。...

weixin_39611722的博客 3455

重学C++系列之虚继承

如何解决C++中的多继承中基类成员重名,产生的二义性,虚继承你值得拥有。

AABond的博客 5071

深信服aCloud虚拟存储VS3.0实战:从数据分片到仲裁机制的全流程解析

本文深度解析了深信服aCloud虚拟存储VS3.0的核心架构与生产级实践。从数据分片、条带化写入、多副本机制到智能缓存分层,详细阐述了其如何实现高性能与高可靠性的平衡。同时,深入剖析了动态均衡、平滑扩容以及故障恢复与仲裁机制,为运维人员和技术决策者提供了在超融合基础架构(HCI)中部署和管理虚拟存储的实战指南。

net55的博客 588

把 ABAP 开发对象装进 XML 行李箱:SAPlink 安装、插件、升级与协作迁移全指南

SAPlink是一款用于在SAP NetWeaver系统间导入导出ABAP对象的工具,特别适合临时协作、系统孤岛等场景。它通过XML文件(Slinkee和Nugget两种格式)传输开发对象,支持插件扩展多种对象类型。安装时需注意版本兼容性、权限和激活流程,使用时要区分覆盖选项的风险。相比正式运输系统,SAPlink更适用于快速共享、原型交付等非生产场景,但需配合命名规范和协作策略确保可维护性。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 151

c++对象的内存布局2--进阶篇---C++ 对象的内存布局(上)

目录(?)[-] 前言对象的影响因素知识复习单一的一般继承多重继承 前言   07年12月,我写了一篇《C++虚函数表解析》的文章,引起了大家的兴趣。有很多朋友对我的文章留了言,有鼓励我的,有批评我的,还有很多问问题的。我在这里一并对大家的留言表示感谢。这也是我为什么再写一篇续言的原因。因为,在上一篇文章中,我用了的示例都是非常简单的,主要是为了说明一些机理上的问题,也是为了图

crazyhacking的专栏 849

Sigrity SPEED2000 TDR TDT Simulation模式如何进行时域阻抗仿真分析操作指导-差分信号

Sigrity SPEED2000 TDR TDT Simulation模式如何进行时域阻抗仿真分析操作指导-单端信号详细介绍了单端信号如何进行TDR仿真分析,下面介绍如何对差分信号进行TDR分析,还是以下图为例进行分析。Impedance是时域阻抗曲线,也是最主要的观测指标,注意:impedance曲线的时间刻度不是延时,延时=时间刻度/2,因为TDR测量的是用一来一回的原理去测量的。Use Lossy peeling 默认不勾选,勾选的话代表同时仿真无损和有损方案,不勾选代表仅仿真有损方案。

weixin_54787054的博客 855

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

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

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

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

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

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

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

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

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

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

C++类对象的内存布局

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

好记性不如写博客! 8992

C++对象的内存布局

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

giantpoplar的专栏 4172

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

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

ljianhui的专栏 1万+

C++ 对象的内存布局

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

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

C++ 对象内存布局

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

tq08g2z的专栏 1458

C++对象内存布局探究

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

xuyouqiang1987的博客 1112

C++类的对象内存布局

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

_程序猿 1235
上一篇: c++对象的内存布局2--进阶篇---C++ 对象的内存布局(上)
下一篇: java速成----一篇博文搞定基础
crazyhacking
博客等级 码龄15年 299粉丝 148原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值