类继承时的内存布局

1. 单继承(无虚函数)

1
2
3
4
5
6
7
8
9
10
11
class Base {
public:
int a;
int b;
};

class Derived : public Base {
public:
int c;
int d;
};

内存布局:

1
2
3
4
5
6
Base对象 (8字节):
┌───────────┐
│ a (4) │
├───────────┤
│ b (4) │
└───────────┘
1
2
3
4
5
6
7
8
9
10
Derived对象 (16字节):
┌───────────┐ ← 起始地址
│ Base::a │
├───────────┤
│ Base::b │
├───────────┤
│ Derived::c│
├───────────┤
│ Derived::d│
└───────────┘

特点:

  • 派生类对象包含完整的基类子对象
  • 派生类新增成员追加在基类成员之后
  • 内存连续,无额外开销

2. 单继承(有虚函数)

1
2
3
4
5
6
7
8
9
10
11
class Base {
public:
virtual void vfunc() {}
int a;
};

class Derived : public Base {
public:
void vfunc() override {}
int b;
};

内存布局(64位系统):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Base对象 (16字节):
┌───────────┐
│ vptr │ → 指向Base虚表 (8字节)
├───────────┤
│ a │ (4字节)
│ padding │ (4字节对齐)
└───────────┘

Derived对象 (24字节):
┌───────────┐
│ vptr │ → 指向Derived虚表 (8字节)
├───────────┤
│ Base::a │ (4字节)
│ padding │ (4字节对齐)
├───────────┤
│ Derived::b│ (4字节)
│ padding │ (4字节对齐)
└───────────┘

虚表结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
Base虚表:
┌──────────────┐
│ RTTI (Base) │
├──────────────┤
│ &Base::vfunc │
└──────────────┘

Derived虚表:
┌───────────────┐
│ RTTI (Derived)│
├───────────────┤
│ &Derived::vfunc│ // 覆盖基类函数
└───────────────┘

3. 多重继承(无虚函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Base1 {
public:
int a;
};

class Base2 {
public:
int b;
};

class Derived : public Base1, public Base2 {
public:
int c;
};

内存布局(32位系统):

1
2
3
4
5
6
7
8
Derived对象 (12字节):
┌───────────┐
│ Base1::a │
├───────────┤
│ Base2::b │
├───────────┤
│ Derived::c│
└───────────┘

4. 多重继承(有虚函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Base1 {
public:
virtual void f1() {}
int a;
};

class Base2 {
public:
virtual void f2() {}
int b;
};

class Derived : public Base1, public Base2 {
public:
void f1() override {}
void f2() override {}
int c;
};

内存布局(64位系统):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Derived对象 (40字节):
┌──────────────┐ ← 起始地址
│ vptr_Base1 │ → 指向Derived的Base1虚表 (8字节)
├──────────────┤
│ Base1::a │ (4字节)
│ padding │ (4字节对齐)
├──────────────┤
│ vptr_Base2 │ → 指向Derived的Base2虚表 (8字节)
├──────────────┤
│ Base2::b │ (4字节)
│ padding │ (4字节对齐)
├──────────────┤
│ Derived::c │ (4字节)
│ padding │ (4字节对齐)
└──────────────┘

虚表结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Base1虚表 (Derived):
┌───────────────┐
│ RTTI (Derived)│
├───────────────┤
│ &Derived::f1 │ // 覆盖Base1::f1
└───────────────┘

Base2虚表 (Derived):
┌───────────────┐
│ this_offset=-16│ // this指针调整值
├───────────────┤
│ RTTI (Derived)│
├───────────────┤
│ &thunk_f2 │ // thunk函数地址
└───────────────┘

thunk_f2汇编代码:
# 目的是在多态的时候,使用非第一个基类的函数时,偏移到子类的完整对象
sub rdi, 16 ; 调整this指针
jmp Derived::f2 ; 跳转到实际函数

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Base {
public:
int a;
};

class Base1 : virtual public Base {
public:
int b;
};

class Base2 : virtual public Base {
public:
int c;
};

class Derived : public Base1, public Base2 {
public:
int d;
};

内存布局(64位系统,典型GCC实现):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Derived对象 (40字节):
┌──────────────┐
│ vptr_Base1 │ → Base1虚表 (8字节)
├──────────────┤
│ Base1::b │ (4字节)
│ padding │ (4字节)
├──────────────┤
│ vptr_Base2 │ → Base2虚表 (8字节)
├──────────────┤
│ Base2::c │ (4字节)
│ padding │ (4字节)
├──────────────┤
│ Derived::d │ (4字节)
│ padding │ (4字节)
├──────────────┤
│ Base::a │ (4字节) ← 共享的虚基类
│ padding │ (4字节)
└──────────────┘

虚表结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
Base1虚表 (Derived):
┌──────────────┐
│ RTTI (Derived)│
├──────────────┤
│ 虚基类偏移=24 │ // 到Base的偏移
└──────────────┘

Base2虚表 (Derived):
┌──────────────┐
│ RTTI (Derived)│
├──────────────┤
│ 虚基类偏移=16 │ // 到Base的偏移
└──────────────┘

6. 带虚函数的菱形继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Base {
public:
virtual void vfunc() {}
int a;
};

class Base1 : virtual public Base {
public:
void vfunc() override {}
int b;
};

class Base2 : virtual public Base {
public:
int c;
};

class Derived : public Base1, public Base2 {
public:
void vfunc() override {}
int d;
};

内存布局(64位系统,Clang实现):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Derived对象 (48字节):
┌──────────────┐
│ vptr_Base1 │ → Derived的Base1虚表 (8)
├──────────────┤
│ Base1::b │ (4)
│ padding │ (4)
├──────────────┤
│ vptr_Base2 │ → Derived的Base2虚表 (8)
├──────────────┤
│ Base2::c │ (4)
│ padding │ (4)
├──────────────┤
│ Derived::d │ (4)
│ padding │ (4)
├──────────────┤
│ vptr_Base │ → Derived的Base虚表 (8)
├──────────────┤
│ Base::a │ (4)
│ padding │ (4)
└──────────────┘

虚表结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Base1虚表 (Derived):
┌───────────────┐
│ RTTI (Derived)│
├───────────────┤
│ 虚基类偏移=32 │ // 到Base子对象
├───────────────┤
│ &Derived::vfunc│ // 覆盖函数
└───────────────┘

Base虚表 (Derived):
┌───────────────┐
│ RTTI (Derived)│
├───────────────┤
│ &Derived::vfunc│ // 最终覆盖
└───────────────┘

7. 内存布局关键点总结

  1. 单继承

    • 派生类包含完整基类子对象
    • 虚表指针在对象起始位置
    • 派生类新增成员追加在末尾
  2. 多重继承

    • 按声明顺序包含多个基类子对象
    • 每个含虚函数的基类有自己的虚表指针
    • 可能需要this指针调整
  3. 虚继承

    • 虚基类子对象在派生类末尾共享
    • 虚派生类包含虚基类指针/偏移信息
    • 解决菱形继承的数据冗余问题
  4. 虚函数影响

    • 每个多态类增加一个虚表指针(vptr)
    • 虚表包含函数指针和元数据(RTTI、偏移量)
    • 派生类虚表覆盖基类函数条目
Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2023-2025 John Doe
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信