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++对象内存结构内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Qt数据库应用之实现通用数据库清理

    Qt数据库应用之实现通用数据库清理

    项目如果需要存储很多日志记录比如运行日志,时间长了记录数量非常多,数据库体积不断增大,对应数据库表的增删改查的效率不断降低,因此需要将早期的数据清理。本文将详细介绍一下通用数据库清理的实现,需要的可以参考一下
    2022-02-02
  • 基于QT设计一个春联自动生成器

    基于QT设计一个春联自动生成器

    春节是中国最隆重的传统节日,一到过年家家户户肯定是要贴春联;在春节前夕,会用大红纸张,加上浓墨书写祝福词语。本文将利用Qt框架设计一个春联自动生成器,需要的可以参考一下
    2022-01-01
  • C++实现LeetCode(187.求重复的DNA序列)

    C++实现LeetCode(187.求重复的DNA序列)

    这篇文章主要介绍了C++实现LeetCode(187.求重复的DNA序列),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • Qt数据库应用之通用数据库同步

    Qt数据库应用之通用数据库同步

    数据库同步的主要功能是将本地的数据库记录同步到远程的数据库。本文将利用Qt实现通用数据库同步功能,感兴趣的小伙伴可以跟随小编一起学习一下
    2022-03-03
  • C语言模拟实现strstr函数的示例代码

    C语言模拟实现strstr函数的示例代码

    strstr是C语言中的函数,作用是返回字符串中首次出现子串的地址。本文将用C语言模拟实现strstr函数,感兴趣的小伙伴可以跟随小编一起学习一下
    2022-07-07
  • 举例讲解C语言的fork()函数创建子进程的用法

    举例讲解C语言的fork()函数创建子进程的用法

    fork函数是Linux下一个近乎专有的C语言函数,因为使用时需要调用unistd.h这个头文件,这里我们就在Linux环境下举例讲解C语言的fork()函数创建子进程的用法,需要的朋友可以参考下
    2016-06-06
  • Linux下Select多路复用实现简易聊天室示例

    Linux下Select多路复用实现简易聊天室示例

    大家好,本篇文章主要讲的是Linux下Select多路复用实现简易聊天室示例,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • C++ STL之slist单向链表容器使用方式

    C++ STL之slist单向链表容器使用方式

    这篇文章主要介绍了C++ STL之slist单向链表容器使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-04-04
  • C语言中求解图形的问题

    C语言中求解图形的问题

    这篇文章主要介绍了C语言中求解图形的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • C++实现顺序表的常用操作(插入删出查找输出)

    C++实现顺序表的常用操作(插入删出查找输出)

    实现顺序表的插入,删除,查找,输出操作在C语言中经常用到。下面小编给大家整理实现代码,一起看下吧
    2016-08-08

最新评论