根據幾篇部落格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 ;
}