根据几篇博客C++对象模型,c++涉及继承和虚继承时的内存布局 ,C++ 对象的内存布局(上) 作了一些归纳和总结,留着备用吧。
讨论问题:
- 单继承内存布局?
- 单继承(虚继承,virtual继承)的布局?
- 多继承内存布局?
- 虚继承内存布局?
1、单继承(非虚继承)
先看单继承的例子:
#include<iostream>
using namespace std;
class Parent
{
public:
int age;
Parent():age(){}
virtual void f(){cout << "Parent::f()" << endl;}
virtual void g(){cout << "Parent::g()" << endl;}
};
class Child : public Parent
{
public:
int num;
Child():num(){}
virtual void f(){cout << "Child::f()" << endl;}
virtual void h(){cout << "Child::h()" << endl;}
virtual void gc(){cout << "Child::gc()" << endl;}
};
class GrandChild : public Child
{
public:
int gpa;
GrandChild() : gpa(){}
virtual void f(){cout << "GrandChild::f()" << endl;}
virtual void ggc(){cout << "GrandChild::ggc()" << endl;}
virtual void gc(){cout << "GrandChild::gc()" << endl;}
};
单继承的内存分布:

单继承(非虚继承)基本上继承父类的 vptr,并且会将自己的类的虚函数直接添加在子类的vptr,是一种 “is A ”的关系,可以共用vptr,而且对象成员变量也相应的继承过来。
2、多继承
using namespace std;
class B
{ public:
int ib;
char cb;
public:
B():ib(0),cb('B') {}
virtual void f() { cout << "B::f()" << endl;}
virtual void Bf() { cout << "B::Bf()" << endl;}
};
class B1 : virtual public B
{
public:
int ib1;
char cb1;
public:
B1():ib1(11),cb1('1') {}
virtual void f() { cout << "B1::f()" << endl;}
virtual void f1() { cout << "B1::f1()" << endl;}
virtual void Bf1() { cout << "B1::Bf1()" << endl;}
};
class B2: virtual public B
{
public:
int ib2;
char cb2;
public:
B2():ib2(12),cb2('2') {}
virtual void f() { cout << "B2::f()" << endl;}
virtual void f2() { cout << "B2::f2()" << endl;}
virtual void Bf2() { cout << "B2::Bf2()" << endl;}
};
class D : public B1, public B2
{
public:
int id;
char cd;
public:
D():id(100),cd('D') {}
virtual void f() { cout << "D::f()" << endl;}
virtual void f1() { cout << "D::f1()" << endl;}
virtual void f2() { cout << "D::f2()" << endl;}
virtual void Df() { cout << "D::Df()" << endl;}
};
多继承的对象分布:
多继承也基本类似单继承,只不过多了几张虚函数表,位置是根据多继承声明的顺序。第一张虚表还安插自己类的虚函数。每个表后面就是继承子类的成员变量。很明显,如果多继承的基类有共同的 “基基类”,必然就有2份“基基类”成员
3、虚继承
如果上面的多继承的基类(B1,B2)虚继承B,那么分布就是如下:
从图上可以看出,多了额外的一张虚表 就是 “基基类”的虚表,虚继承是一种“has A”的关系,不是is-a关系,会保存自己的一张虚表和成员,后续派生的数据成员也是只有一份。
注意:VS上,在B1和B2的 vptr和成员之间位置,会添加虚基类指针vbptr(可能是偏移地址或者直接地址),而clang和GCC没有,其他编译器实现不清楚,有些编译器可能将虚基类的偏移地址 安插到虚函数表指针中(正偏移为虚函数,负为虚基类)
虚继承B1和B2的对象分布
这里对于虚线框处,inside object C++ model 说对于虚基类会有一个 基类表指针,指向虚基类的偏移或者虚基类的地址,这里本人在实验的时候,并没有发现 vbptr,vs似乎会生成在vptr和成员变量之间。
棕色部分根据编译器实现而定,vs上似乎会生成(放在vptr和成员变量之间),gcc和clang没有这部分。
注意:VS上,在B1和B2的 vptr和成员之间位置,会添加虚基类指针vbptr(可能是偏移地址或者直接地址),而clang和GCC没有,其他编译器实现不清楚,有些编译器可能将虚基类的偏移地址 安插到虚函数表指针中(正偏移为虚函数,负为虚基类)
测试代码:
机器在64位:指针为8bytes,而且按8bytes对齐方式
#include <iostream>
using namespace std;
class Base{
public:
virtual void f(){cout<<"Base::f()";}
virtual void g(){cout<<"Base::g()";}
virtual void h(){cout<<"Base::h()";}
long b=;
};
class Base2:virtual Base{
public:
virtual void f(){cout<<"Base2::f()";}
virtual void g(){cout<<"Base2::g()";}
virtual void gb2(){cout<<"Base2::gb2()";}
long b=;
};
class Base3:virtual Base{
public:
virtual void f(){cout<<"Base3::f()";}
virtual void g(){cout<<"Base3::g()";}
virtual void gb3(){cout<<"Base3::gb3()";}
long b=;
};
class Derive:public Base2,public Base3{
public:
virtual void f(){cout<<"Derive::f()";}
virtual void g(){cout<<"Derive::g()";}
virtual void gb4(){cout<<"Derive::gb4()";};
long b=;
};
int main() {
//shit 这里是有8字节对齐,int类型存放也是8字节对齐
/* using Fun= void (*)();
Fun fun= nullptr;
Derive d;
cout<< "虚函数表地址:"<<(long *)&d<<endl;
cout<<"虚表-第一个函数地址:"<<(long*)*(long*)&d<<endl;
//int* b=(int*)((long*)&d+3); //b 地址(如果是int会进行补齐)
long*b=(long*)&d+1;
cout<<*b<<" "<<*(b+1)<<endl;
long **b2=(long**)((long*)&d+2);//函数表地址
fun=(Fun)b2[0][2];
fun();*/
using Fun= void (*)();
Fun fun= nullptr;
Base2 b2;
long*b=(long*)&b2+;
//cout<<*b<<" "<<*(b+1)<<endl;
long** f=(long**)((long*)&b2 + );
cout<< sizeof(b2);
return ;
}