对象内存布局(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
B的虚表中存放B的虚函数地址
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位置为偏移表指针所在位置
B的虚表中存放B的虚函数地址
A的虚表中存放B继承的的虚函数地址
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标志
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的虚表合并
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其余与单继承相同
)
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
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
总结一下
派生类成员在基类成员之后,在虚基类成员之前。
在基类有虚函数的时候,会在该基类及每个该基类的派生类生成一个虚表指针,虚表指针为该类的首个成员,虚表指针指向该类的虚函数地址数组。
在虚继承时,同时还会在派生类生成一个偏移表指针(指向偏移表,用于定位虚基类位置),偏移表第一项为自身偏移,第一项为第一个虚基类的偏移,第二项为第二个虚基类的偏移,偏移表的值于偏移表指针相加即可得到自己或父类的内存地址。