对象内存布局 (12)

简介: 下面来看看虚基类对对象内存布局的影响。虚基类的主要作用就是在所有的派生类中,保留且仅保留一份虚基类的suboject。   a. 一个虚基类的情况 #include using namespace std; class Base { public: int base...

下面来看看虚基类对对象内存布局的影响。虚基类的主要作用就是在所有的派生类中,保留且仅保留一份虚基类的suboject。

 

a. 一个虚基类的情况

#include <iostream>
using namespace std;

class Base
{
public:
    int base_member;
};
class Derived : public virtual Base {};

int main(void)
{
    Base b;
    Derived d;
    cout << sizeof(b) << endl;
    cout << sizeof(d) << endl;
    return 0;
}

运行结果:

注意使用了虚继承,即class Derived : public virtual Base。这次Derived的对象大小为什么为8 bytes呢?这是因为编译器会给Derived对象安插一个虚基类表指针vbptr,下面给出Derived对象的memory layout:

 

 

虚基类表指针vbptr指向Derived类的virtual bass class table(虚基类表),虚基类表中存放的是Derived类的虚基类表指针到虚基类实例指针的偏移量

 在main函数的return前,增加如下语句:

#include <iostream>
using namespace std;

class Base
{
public:
    int base_member;
};

class Derived : public virtual Base {};

int main(void)
{
    Base b;
    Derived d;
    cout << sizeof(b) << endl;
    cout << sizeof(d) << endl;
    cout << "Derived object d's vbptr = " << (unsigned long*)(&d) << endl;

    cout << "Address of virtual base class table = " << (unsigned long*)*(unsigned long*)(&d) << endl;

    cout << "Item 1 in virtual base class table = " << *(unsigned long*)*(unsigned long*)(&d) << endl;

    cout << "Item 2 in virtual base class table = " << *((unsigned long*)*(unsigned long*)(&d) + 1) << endl;

    cout << "Item 3 in virtual base class table = " << *((unsigned long*)*(unsigned long*)(&d) + 2) << endl;

    cout << "The address of virtual base class Base = " << (Base*)(&d) << endl;
    return 0;
}

编译后运行结果:

编译后运行结果:

不难发现,虚基类示例地址 = vbptr + offset,即0x0012FF78 = 0x0012FF74 + 4。图示如下:

b. 我们来看看Derived有两个虚基类的情况:

#include <iostream>
using namespace std;

class Base1
{
public:
    int base1_member;
};
class Base2
{
public:

    int base2_member;
};

class Derived : public virtual Base1, public virtual Base2 {};

int main(void)
{
    Base1 b1;

    Base2 b2;

    Derived d;

    cout << sizeof(b1) << endl;

    cout << sizeof(b2) << endl;

    cout << sizeof(d) << endl;

    cout << "Derived object d's vbptr = " << (unsigned long*)(&d) << endl;

    cout << "Address of virtual base class table = " << (unsigned long*)*(unsigned long*)(&d) << endl;

    cout << "Item 1 in virtual base class table = " << *((unsigned long*)*(unsigned long*)(&d) + 0) << endl;

    cout << "Item 2 in virtual base class table = " << *((unsigned long*)*(unsigned long*)(&d) + 1) << endl;

    cout << "Item 3 in virtual base class table = " << *((unsigned long*)*(unsigned long*)(&d) + 2) << endl;

    cout << "Item 3 in virtual base class table = " << *((unsigned long*)*(unsigned long*)(&d) + 3) << endl;

    cout << "The address of virtual base class: Base1's instance = " << (Base1*)(&d) << endl;

    cout << "The address of virtual base class: Base2's instance = " << (Base2*)(&d) << endl;

    return 0;

}

编译运行结果如下:

 

Derived对象的memory layout图解如下:

 

 

不管Derived类有多少个虚基类,它只有一个vbptr和一个virtual base class table

相关文章
|
2月前
|
存储 编译器 程序员
【C语言】内存布局大揭秘 ! -《堆、栈和你从未听说过的内存角落》
在C语言中,内存布局是程序运行时非常重要的概念。内存布局直接影响程序的性能、稳定性和安全性。理解C程序的内存布局,有助于编写更高效和可靠的代码。本文将详细介绍C程序的内存布局,包括代码段、数据段、堆、栈等部分,并提供相关的示例和应用。
74 5
【C语言】内存布局大揭秘 ! -《堆、栈和你从未听说过的内存角落》
|
2月前
|
机器学习/深度学习 人工智能 缓存
【AI系统】推理内存布局
本文介绍了CPU和GPU的基础内存知识,NCHWX内存排布格式,以及MNN推理引擎如何通过数据内存重新排布进行内核优化,特别是针对WinoGrad卷积计算的优化方法,通过NC4HW4数据格式重排,有效利用了SIMD指令集特性,减少了cache miss,提高了计算效率。
79 3
|
2月前
|
缓存 监控 算法
Python内存管理:掌握对象的生命周期与垃圾回收机制####
本文深入探讨了Python中的内存管理机制,特别是对象的生命周期和垃圾回收过程。通过理解引用计数、标记-清除及分代收集等核心概念,帮助开发者优化程序性能,避免内存泄漏。 ####
64 3
|
3月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
252 4
|
4月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
177 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
4月前
|
Java 测试技术 Android开发
让星星⭐月亮告诉你,强软弱虚引用类型对象在内存足够和内存不足的情况下,面对System.gc()时,被回收情况如何?
本文介绍了Java中四种引用类型(强引用、软引用、弱引用、虚引用)的特点及行为,并通过示例代码展示了在内存充足和不足情况下这些引用类型的不同表现。文中提供了详细的测试方法和步骤,帮助理解不同引用类型在垃圾回收机制中的作用。测试环境为Eclipse + JDK1.8,需配置JVM运行参数以限制内存使用。
58 2
|
4月前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
84 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
4月前
|
存储 Java
深入理解java对象的内存布局
这篇文章深入探讨了Java对象在HotSpot虚拟机中的内存布局,包括对象头、实例数据和对齐填充三个部分,以及对象头中包含的运行时数据和类型指针等详细信息。
40 0
深入理解java对象的内存布局
|
4月前
|
算法 Java
JVM进阶调优系列(3)堆内存的对象什么时候被回收?
堆对象的生命周期是咋样的?什么时候被回收,回收前又如何流转?具体又是被如何回收?今天重点讲对象GC,看完这篇就全都明白了。
|
4月前
|
存储 编译器 C++
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作(三)
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作