C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++对象内存结构

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

作者: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),用于支持多态特性。

核心概念:

示例:

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

内存结构:

偏移量  内容
0-7     vptr(8字节,指向Animal的vtable)
8-11    weight(4字节,int类型)

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

继承会导致派生类对象包含基类子对象(基类的成员变量+可能的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的成员)

多继承可能导致指针调整:当用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++对象内存结构内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文