对象内存布局(MSVC2017)

来源:互联网 发布:windows安装centos引导 编辑:程序博客网 时间:2024/06/06 20:08

对象内存布局(MSVC2017)

本文主要介绍c++对象在内存中的分布

class B 直接继承于 class A

class A{public:    A() { m_A = 1; }    virtual ~A() {}    virtual void fun1() {}public:    int m_A;};class B : public A{public:    B() { m_B = 2; }    virtual ~B() {}    virtual void fun1() {}    virtual void fun2() {}public:    int m_B;};

内存结构:
+0x00 指向B的虚表指针
+0x04 基类A的成员m_A
+0x08 B的成员m_B

1

B的虚表中存放B的虚函数地址

2

3

4

class B 虚继承于 class A

class A{public:    A() { m_A = 1; }    virtual ~A() {}    virtual void fun1() {}public:    int m_A;};class B : virtual public A{public:    B() { m_B = 2; }    virtual ~B() {}    virtual void fun2() {}public:    int m_B;};

内存结构:
+0x00 指向B的虚表指针
+0x04 指向偏移表的指针
+0x08 B的成员m_B
+0x0C 虚基类A的虚表指针
+0x10 虚基类A的成员m_A

偏移表第一项为自身的偏移-4
第二项为虚基类A的偏移8
偏移0位置为偏移表指针所在位置

5

B的虚表中存放B的虚函数地址

6

A的虚表中存放B继承的的虚函数地址

8

9

class B 虚继承于 class A 且覆盖A的虚函数

class A{public:    A() { m_A = 1; }    virtual ~A() {}    virtual void fun1() {}public:    int m_A;};class B : virtual public A{public:    B() { m_B = 2; }    virtual ~B() {}    virtual void fun1() {}    virtual void fun2() {}public:    int m_B;};

会在虚基类前添加一个全0标志

10

class C 继承于 class A 和 class B

class A{public:    A() { m_A = 1; }    virtual ~A() {}    virtual void fun1() {}public:    int m_A;};class B{public:    B() { m_B = 2; }    virtual ~B() {}    virtual void fun2() {}public:    int m_B;};class C : public A, public B{public:    C() { m_C = 3; }    virtual ~C() {}    virtual void fun3() {}public:    int m_C;};

内存结构:
+0x00 指向A的虚表的指针
+0x04 A的成员m_A
+0x08 指向B的虚表的指针
+0x0C B的成员m_B
+0x10 C的成员m_C
其中C的虚函数地址存放在A的虚表之后,与A的虚表合并

11

class C 虚继承于 class A 和 class B

class A{public:    A() { m_A = 1; }    virtual ~A() {}    virtual void fun1() {}public:    int m_A;};class B{public:    B() { m_B = 2; }    virtual ~B() {}    virtual void fun2() {}public:    int m_B;};class C : virtual public A, virtual public B{public:    C() { m_C = 3; }    virtual ~C() {}    virtual void fun3() {}public:    int m_C;};

内存结构:
+0x00 指向C的虚表的指针
+0x04 指向偏移表的指针
+0x08 C的成员m_C
+0x0C 虚基类A的虚表指针
+0x10 虚基类的成员m_A
+0x14 虚基类B的虚表指针
+0x18 虚基类B的成员m_B

其余与单继承相同

![12](12.png)

class C、class B继承于 class A,class D 继承于class C、class B(菱形继承)

class A{public:    A() { m_A = 1; }    virtual ~A() {}    virtual void fun1() {}public:    int m_A;};class B : public A{public:    B() { m_B = 2; }    virtual ~B() {}    virtual void fun2() {}public:    int m_B;};class C : public A{public:    C() { m_C = 3; }    virtual ~C() {}    virtual void fun3() {}public:    int m_C;};class D : public B, public C{public:    D() { m_D = 4; }    virtual ~D() {}    virtual void fun4() {}public:    int m_D;};

内存结构:
+0x00 指向A的虚表的指针

其中存放A,B,D的虚函数地址

+0x04 A的成员m_A
+0x08 B的成员m_B
+0x0C 指向A的虚表的指针
其中存放A,C的虚函数地址
+0x10 A的成员m_A
+0x14 C的成员m_C
+0x18 D的成员m_D

14

class C、class B虚继承于 class A,class D 虚继承于class C、class B(菱形虚继承)

class A{public:    A() { m_A = 1; }    virtual ~A() {}    virtual void fun1() {}public:    int m_A;};class B : virtual public A{public:    B() { m_B = 2; }    virtual ~B() {}    virtual void fun2() {}public:    int m_B;};class C : virtual public A{public:    C() { m_C = 3; }    virtual ~C() {}    virtual void fun3() {}public:    int m_C;};class D : virtual public B, virtual public C{public:    D() { m_D = 3; }    virtual ~D() {}    virtual void fun4() {}public:    int m_D;};

内存结构:
+0x00 指向D的虚表的指针
+0x04 指向D的偏移表的指针
+0x08 D的成员m_D
+0x0C 虚基类A的虚表指针
+0x10 虚基类的成员m_A
+0x14 虚基类B的虚表指针
+0x18 虚基类B的偏移表指针
+0x1C 虚基类B的成员m_B
+0x20 虚基类C的虚表指针
+0x18 虚基类C的偏移表指针
+0x1C 虚基类C的成员m_C

13

总结一下

派生类成员在基类成员之后,在虚基类成员之前。
在基类有虚函数的时候,会在该基类及每个该基类的派生类生成一个虚表指针,虚表指针为该类的首个成员,虚表指针指向该类的虚函数地址数组。
在虚继承时,同时还会在派生类生成一个偏移表指针(指向偏移表,用于定位虚基类位置),偏移表第一项为自身偏移,第一项为第一个虚基类的偏移,第二项为第二个虚基类的偏移,偏移表的值于偏移表指针相加即可得到自己或父类的内存地址。

原创粉丝点击