C++对象的内存结构的实现

 更新时间:2026年01月29日 10:42:42   作者:bkspiderx  
本文主要介绍了C++对象的内存结构的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

在C++中,对象的内存结构并非由语言标准强制规定具体细节(不同编译器可能有细微差异),但遵循一些通用规则。其内存布局主要受成员变量虚函数继承关系等因素影响,核心是为了高效存储数据和支持面向对象的核心特性(如封装、继承、多态)。

一、基础情况:无继承、无虚函数的对象

对于一个仅包含非静态成员变量的简单类,对象的内存结构本质上是成员变量的有序集合,不包含成员函数(成员函数存放在代码区,被所有对象共享)。

示例:

class Person {
private:
    int age;         // 4字节
    char gender;     // 1字节
    double height;   // 8字节
public:
    void print() {}  // 成员函数,不占对象内存
};

内存结构:

对象的内存中仅包含 agegenderheight 三个成员变量,按声明顺序排列,但会受到内存对齐影响(编译器为提高访问效率,会在成员间插入填充字节)。

假设按8字节对齐(常见编译器默认),内存布局可能为:

偏移量  内容
0-3     age(4字节)
4-7     填充(4字节,因gender仅1字节,需对齐到下一个8字节边界)
8-8     gender(1字节)
9-15    填充(7字节,为height的8字节对齐)
16-23   height(8字节)

总大小:24字节(而非4+1+8=13字节,填充字节是为了让CPU更高效地访问多字节类型)。

二、含静态成员的对象

静态成员(变量/函数)属于类级别的共享资源,不存储在对象的内存中,而是存放在全局数据区(或静态存储区),所有对象共享同一份。

示例:

class Person {
private:
    int age;             // 非静态成员,属于对象
    static int count;    // 静态成员,不属于对象
public:
    static void incr() {} // 静态函数,不属于对象
};
int Person::count = 0; // 静态成员的定义

内存结构:

对象的内存中只包含非静态成员agecountincr()存放在全局区,与对象内存无关。

三、含虚函数的对象(支持多态)

当类中包含虚函数(或继承了虚函数)时,对象会额外包含一个虚函数表指针(vptr),用于支持多态特性。

核心概念:

  • 虚函数表(vtable):每个包含虚函数的类会有一个全局唯一的vtable,本质是一个函数指针数组,存储该类所有虚函数的地址。
  • 虚函数表指针(vptr):每个对象都会包含一个vptr(通常在对象内存的开头),指向所属类的vtable。当通过基类指针调用虚函数时,编译器会通过vptr找到vtable,再调用实际的函数(实现动态绑定)。

示例:

class Animal {
public:
    virtual void eat() {} // 虚函数
    int weight;           // 成员变量
};

内存结构:

偏移量  内容
0-7     vptr(8字节,指向Animal的vtable)
8-11    weight(4字节,int类型)
  • vtable的内容:[&Animal::eat](存储eat()函数的地址)。
  • 若派生类重写了虚函数:
    class Cat : public Animal {
    public:
        void eat() override {} // 重写虚函数
    };
    
    Cat类的vtable会替换为[&Cat::eat],Cat对象的vptr指向该vtable,实现“基类指针调用派生类函数”的多态。

四、继承关系下的对象内存结构

继承会导致派生类对象包含基类子对象(基类的成员变量+可能的vptr),再加上派生类自己的成员变量。

1. 单继承(无虚函数覆盖)

class Base {
public:
    int a;
};
class Derived : public Base {
public:
    int b;
};

Derived对象的内存结构:

偏移量  内容
0-3     a(基类成员)
4-7     b(派生类成员)

2. 单继承(含虚函数)

class Base {
public:
    virtual void f() {}
    int a;
};
class Derived : public Base {
public:
    virtual void g() {} // 新增虚函数
    int b;
};

Derived对象的内存结构:

偏移量  内容
0-7     vptr(指向Derived的vtable)
8-11    a(基类成员)
12-15   b(派生类成员)

Derived的vtable内容:[&Base::f, &Derived::g](包含基类和派生类的虚函数)。

3. 多继承(最复杂的情况)

多继承时,派生类对象会包含所有基类的子对象,每个基类若有虚函数,可能对应一个vptr(导致对象内存中存在多个vptr)。

示例:

class Base1 {
public:
    virtual void f() {}
    int a;
};
class Base2 {
public:
    virtual void g() {}
    int b;
};
class Derived : public Base1, public Base2 {
public:
    int c;
};

Derived对象的内存结构(可能):

偏移量  内容
0-7      vptr1(指向Derived的vtable1,对应Base1的虚函数)
8-11     a(Base1的成员)
12-19    vptr2(指向Derived的vtable2,对应Base2的虚函数)
20-23    b(Base2的成员)
24-27    c(Derived的成员)

  • vtable1:[&Derived::f](继承自Base1的虚函数)
  • vtable2:[&Derived::g](继承自Base2的虚函数)

多继承可能导致指针调整:当用Base2*指向Derived对象时,指针会自动偏移到Base2子对象的起始位置(上例中偏移12字节)。

4. 虚继承(解决菱形继承问题)

菱形继承(两个派生类继承同一基类,再被一个类继承)会导致基类成员在最终派生类中存在多份副本(数据冗余+二义性)。虚继承通过让基类子对象只存一份解决此问题。

示例(菱形继承):

class A { public: int x; };
class B : virtual public A { public: int y; }; // 虚继承A
class C : virtual public A { public: int z; }; // 虚继承A
class D : public B, public C { public: int w; };

D对象的内存结构(简化):

偏移量  内容
0-7      vptr_B(B的虚表指针)
8-11     y(B的成员)
12-19    vptr_C(C的虚表指针)
20-23    z(C的成员)
24-27    w(D的成员)
28-31    x(A的成员,仅一份,被B和C共享)

  • 虚继承通过在派生类中添加“虚基类指针”(或偏移量)定位共享的基类子对象,避免数据冗余。

总结

C++对象的内存结构核心由以下部分构成(按影响优先级):

  1. 非静态成员变量:按声明顺序排列,受内存对齐影响。
  2. 虚函数表指针(vptr):当类有虚函数时存在,用于多态,数量取决于继承关系(多继承可能有多个vptr)。
  3. 基类子对象:继承时包含,顺序与继承声明一致,虚继承会优化为共享副本。

静态成员、成员函数不占用对象内存,存放在全局区或代码区。不同编译器(如GCC、MSVC)在细节上(如vptr位置、对齐规则)可能有差异,但核心逻辑一致。

到此这篇关于C++对象的内存结构的实现的文章就介绍到这了,更多相关C++对象内存结构内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言实现输入两个数字将其按从小到大输出的方法

    C语言实现输入两个数字将其按从小到大输出的方法

    这篇文章主要介绍了C语言实现输入两个数字将其按从小到大输出的方法,本文通过代码讲解的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • 浅谈C++重载、重写、重定义

    浅谈C++重载、重写、重定义

    下面小编就为大家带来一篇浅谈C++重载、重写、重定义。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06
  • C++使用OpenCV实现证件照蓝底换成白底功能(或其他颜色如红色)详解

    C++使用OpenCV实现证件照蓝底换成白底功能(或其他颜色如红色)详解

    这篇文章主要介绍了C++使用OpenCV实现证件照蓝底换成白底功能(或其他颜色如红色),结合实例形式详细分析了OpenCV颜色转换相关操作原理与实现技巧,需要的朋友可以参考下
    2019-12-12
  • C++实现简易计算器功能

    C++实现简易计算器功能

    这篇文章主要为大家详细介绍了C++实现简易计算器功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • C语言深入分析递归函数的实现

    C语言深入分析递归函数的实现

    递归(recursive)函数是“自己调用自己”的函数,无论是采用直接或间接调用方式。间接递归意味着函数调用另一个函数(然后可能又调用第三个函数等),最后又调用第一个函数。因为函数不可以一直不停地调用自己,所以递归函数一定具备结束条件
    2022-04-04
  • C++中的覆盖和隐藏详解

    C++中的覆盖和隐藏详解

    这篇文章主要介绍了C++中重载、重写(覆盖)和隐藏的区别,是C++面向对象程序设计非常重要的概念,需要的朋友可以参考下,希望能够给你带来帮助
    2021-08-08
  • C++的new和delete使用示例详解

    C++的new和delete使用示例详解

    这篇文章主要为大家介绍了C++的new和delete使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • C语言入门篇--注释,关键字typedef及转义字符详解

    C语言入门篇--注释,关键字typedef及转义字符详解

    本篇文章是c语言基础篇,主要为大家介绍了C语言的关键字typedef,注释,转义字符的基本理论知识,希望可以帮助大家快速入门c语言的世界,更好的理解c语言
    2021-08-08
  • C++11右值引用和移动语义的实例解析

    C++11右值引用和移动语义的实例解析

    左值和右值都是针对表达式,左值是指表达式结束后依然存在的持久对象,右值是指表达式结束时就不再存在的临时对象,下面这篇文章主要给大家介绍了关于C++11右值引用和移动语义的相关资料,需要的朋友可以参考下
    2022-09-09
  • 利用C语言实现顺序表的实例操作

    利用C语言实现顺序表的实例操作

    顺序表是线性表中的一种重要的数据结构,也是最基础的数据结构,所以他不仅是学习中的重点,也是应用开发非常常用的一种数据结构。这篇文章介绍如何利用C语言实现顺序表。
    2016-08-08

最新评论