- 編程
- cpp
預知識
本文的內(nèi)容使用的是32位的編譯器編譯出的結(jié)果亏推,可以打印出類的內(nèi)存布局信息
DevCPP IDE
這個IDE是我比較喜歡的windows下的cpp的IDE之一汇荐,它有一個工具->編譯選項他爸,可以選擇編譯器類型,也可以在編譯選項中加入一些信息,為了能夠輸出內(nèi)存布局信息瓜饥,我在編譯時加入以下命令
--std=c++11 -fdump-class-hierarchy
-fdump-class-hierarchy 這個選項能夠在gcc編譯時生成類的布局信息,生成的文件名類似為6.cpp.002t.class
vs2017
vs使用的編譯器是cl浴骂,它的命令為
/d1reportAllClassLayout: 輸出所有類相關布局
clang
clang -Xclang -fdump-record-layouts
理解內(nèi)存布局信息
測試代碼如下
class father{
int a;
int b;
};
class child: public father{
};
int main(){
return 0;
}
使用 g++ -fdump-class-hierarchy test.cpp 生成了 test.cpp.002t.class內(nèi)容如下
Class father
size=8 align=4
base size=8 base align=4
father (0x0x16662d8) 0
Class child
size=8 align=4
base size=8 base align=4
child (0x0x3984e00) 0
father (0x0x1666310) 0
顯示了類的大小和對齊信息乓土。
題話外: 理解內(nèi)存對齊
- 結(jié)構(gòu)體第一個成員的偏移量(offset)為0,以后每個成員相對于結(jié)構(gòu)體首地址的 offset 都是該成員大小與有效對齊值中較小那個的整數(shù)倍溯警,如有需要編譯器會在成員之間加上填充字節(jié)趣苏。
- 結(jié)構(gòu)體的總大小為 有效對齊值 的整數(shù)倍,如有需要編譯器會在最末一個成員之后加上填充字節(jié)梯轻。
多態(tài)與虛表
多態(tài)食磕,簡單來說,是指在繼承層次中喳挑,父類的指針可以具有多種形態(tài)——當它指向某個子類對象時彬伦,通過它能夠調(diào)用到子類的函數(shù),而非父類的函數(shù)伊诵。
虛函數(shù)指針一般都放在對象內(nèi)存布局的第一個位置上媚朦,這是為了保證在多層繼承或多重繼承的情況下能以最高效率取到虛函數(shù)表。當vprt位于對象內(nèi)存最前面時日戈,對象的地址即為虛函數(shù)指針地址询张。我們可以取得虛函數(shù)指針的地址。
下面的方式取得虛函數(shù)指針的地址(而非虛函數(shù)指針指向的地址)
Base b(1000);
int * vptrAdree = (int *)(&b);
vptrAdree指向了虛函數(shù)指針(指向虛函數(shù)表)浙炼,
而我們可以通過如下的方式獲得第一個虛函數(shù)的地址
Base b(1000);
using fun=void(*) ();
fun fun1 = (fun)*((int *)*(int *)(&b));
fun fun2 = (fun)*((int *)*(int *)(&c)+1);
簡單的情況份氧,單層繼承與虛函數(shù)全覆蓋
通過查看編譯器生成的內(nèi)存布局信息來具體地看:
#include <iostream>
using namespace std;
class Base{
int a;
virtual int print(){
cout<<" i am base"<<endl;
}
virtual int print2(){
cout<<" i am base2" <<endl;
}
};
class child: public Base{
int print(){
cout<< " i am the child" <<endl;
}
int print2(){
cout<<" i am the child2" <<endl;
}
};
int main(){
Base testBase;
//using fun=int(*)();
typedef int(*fun) (void);
fun print=(fun)*((int *)*(int *)(&testBase));
print();
cout<<(int *)*(int *)(&testBase)<<endl;
cout<<((int *)*(int *)(&testBase))+1<<endl;
fun pprint2 = (fun)*((int *)*(int *)(&testBase)+1);
pprint2();
return 0;
}
g++編譯后生成的內(nèi)存布局信息為:
Vtable for Base
Base::_ZTV4Base: 4u entries
0 (int (*)(...))0
4 (int (*)(...))(& _ZTI4Base)
8 (int (*)(...))Base::print
12 (int (*)(...))Base::print2
Class Base
size=8 align=4
base size=8 base align=4
Base (0x0x4e057a8) 0
vptr=((& Base::_ZTV4Base) + 8u)
Vtable for child
child::_ZTV5child: 4u entries
0 (int (*)(...))0
4 (int (*)(...))(& _ZTI5child)
8 (int (*)(...))child::print
12 (int (*)(...))child::print2
Class child
size=8 align=4
base size=8 base align=4
child (0x0x4e4b280) 0
vptr=((& child::_ZTV5child) + 8u)
Base (0x0x4e057e0) 0
primary-for child (0x0x4e4b280)
Vtable表示的是虛函數(shù)表,可以發(fā)現(xiàn)Vtable for Base里面保存了vptr弯屈, 而且位置是vptr=((& Base::_ZTV4Base) + 8u)(注意這里加了8的偏移)蜗帜,這個位置的第一個內(nèi)容就是(int (*)(...))Base::print。
復雜的情況:多重繼承與部分虛函數(shù)覆蓋
#include <iostream>
using namespace std;
class Parent {
public:
int iparent;
Parent ():iparent (10) {}
virtual void g() { cout << " Parent::g()" << endl; }
virtual void f() { cout << " Parent::f()" << endl; }
virtual void h() { cout << " Parent::h()" << endl; }
};
class Child : public Parent {
public:
int ichild;
Child():ichild(100) {}
virtual void g_child() { cout << "Child::g_child()" << endl; }
virtual void h_child() { cout << "Child::h_child()" << endl; }
virtual void f() { cout << "Child::f()" << endl; }
};
class GrandChild : public Child{
public:
int igrandchild;
GrandChild():igrandchild(1000) {}
virtual void g_child() { cout << "GrandChild::g_child()" << endl; }
virtual void f() { cout << "GrandChild::f()" << endl; }
virtual void h_grandchild() { cout << "GrandChild::h_grandchild()" << endl; }
};
int main(){
typedef void(*Fun)(void);
GrandChild gc;
int** pVtab = (int**)&gc;
cout << "[0] GrandChild::_vptr->" << endl;
for (int i=0; (Fun)pVtab[0][i]!=NULL; i++){
Fun pFun = (Fun)pVtab[0][i];
cout << " ["<<i<<"] ";
pFun();
}
cout << "[1] Parent.iparent = " << (int)pVtab[1] << endl;
cout << "[2] Child.ichild = " << (int)pVtab[2] << endl;
cout << "[3] GrandChild.igrandchild = " << (int)pVtab[3] << endl;
}
內(nèi)存布局信息如下
Vtable for Parent
Parent::_ZTV6Parent: 5u entries
0 (int (*)(...))0
4 (int (*)(...))(& _ZTI6Parent)
8 (int (*)(...))Parent::g
12 (int (*)(...))Parent::f
16 (int (*)(...))Parent::h
Class Parent
size=8 align=4
base size=8 base align=4
Parent (0x0x4de37a8) 0
vptr=((& Parent::_ZTV6Parent) + 8u)
Vtable for Child
Child::_ZTV5Child: 7u entries
0 (int (*)(...))0
4 (int (*)(...))(& _ZTI5Child)
8 (int (*)(...))Parent::g
12 (int (*)(...))Child::f
16 (int (*)(...))Parent::h
20 (int (*)(...))Child::g_child
24 (int (*)(...))Child::h_child
Class Child
size=12 align=4
base size=12 base align=4
Child (0x0x4e2a5c0) 0
vptr=((& Child::_ZTV5Child) + 8u)
Parent (0x0x4de37e0) 0
primary-for Child (0x0x4e2a5c0)
Vtable for GrandChild
GrandChild::_ZTV10GrandChild: 8u entries
0 (int (*)(...))0
4 (int (*)(...))(& _ZTI10GrandChild)
8 (int (*)(...))Parent::g
12 (int (*)(...))GrandChild::f
16 (int (*)(...))Parent::h
20 (int (*)(...))GrandChild::g_child
24 (int (*)(...))Child::h_child
28 (int (*)(...))GrandChild::h_grandchild
Class GrandChild
size=16 align=4
base size=16 base align=4
GrandChild (0x0x4e2aa80) 0
vptr=((& GrandChild::_ZTV10GrandChild) + 8u)
Child (0x0x4e2aac0) 0
primary-for GrandChild (0x0x4e2aa80)
Parent (0x0x4de3818) 0
primary-for Child (0x0x4e2aac0)
通過查看可以發(fā)現(xiàn)资厉,子類的虛函數(shù)表里面的優(yōu)先級是先父類厅缺,再子類再孫類,同一優(yōu)先級按照聲明順序排地址,即使父類的虛函數(shù)被覆蓋了湘捎,也要寫在原來的位置诀豁,這樣能夠保證,父類的指針能按照函數(shù)名找到那個地址窥妇。
程序的執(zhí)行結(jié)果為:
[0] GrandChild::_vptr->
<pre> [0] GrandChild::f()
[1] Parent::g()
[2] Parent::h()
[3] GrandChild::g_child()
[4] Child::h1()
[5] GrandChild::h_grandchild()
[1] Parent.iparent = 10
[2] Child.ichild = 100
[3] GrandChild.igrandchild = 1000
可知
- 虛函數(shù)表在最前面的位置舷胜。
- 成員變量根據(jù)其繼承和聲明順序依次放在后面。
- 在單一的繼承中活翩,被overwrite的虛函數(shù)在虛函數(shù)表中得到了更新烹骨。
多重繼承
#include <iostream>
using namespace std;
class Base1 {
public:
int ibase1;
Base1():ibase1(10) {}
virtual void f() { cout << "Base1::f()" << endl; }
virtual void g() { cout << "Base1::g()" << endl; }
virtual void h() { cout << "Base1::h()" << endl; }
};
class Base2 {
public:
int ibase2;
Base2():ibase2(20) {}
virtual void f() { cout << "Base2::f()" << endl; }
virtual void g() { cout << "Base2::g()" << endl; }
virtual void h() { cout << "Base2::h()" << endl; }
};
class Base3 {
public:
int ibase3;
Base3():ibase3(30) {}
virtual void f() { cout << "Base3::f()" << endl; }
virtual void g() { cout << "Base3::g()" << endl; }
virtual void h() { cout << "Base3::h()" << endl; }
};
class Derive : public Base1, public Base2, public Base3 {
public:
int iderive;
Derive():iderive(100) {}
virtual void f() { cout << "Derive::f()" << endl; }
virtual void g1() { cout << "Derive::g1()" << endl; }
};
int main(){
typedef void(*Fun)(void);
Derive d;
int** pVtab = (int**)&d;
cout << "[0] Base1::_vptr->" << endl;
Fun pFun = (Fun)pVtab[0][0];
cout << " [0] ";
pFun();
pFun = (Fun)pVtab[0][1];
cout << " [1] ";pFun();
pFun = (Fun)pVtab[0][2];
cout << " [2] ";pFun();
pFun = (Fun)pVtab[0][3];
cout << " [3] "; pFun();
pFun = (Fun)pVtab[0][4];
cout << " [4] "; cout<<pFun<<endl;
cout << "[1] Base1.ibase1 = " << (int)pVtab[1] << endl;
int s = sizeof(Base1)/4;
cout << "[" << s << "] Base2::_vptr->"<<endl;
pFun = (Fun)pVtab[s][0];
cout << " [0] "; pFun();
pFun = (Fun)pVtab[s][1];
cout << " [1] "; pFun();
pFun = (Fun)pVtab[s][2];
cout << " [2] "; pFun();
pFun = (Fun)pVtab[s][3];
cout << " [3] ";
cout<<pFun<<endl;
cout << "["<< s+1 <<"] Base2.ibase2 = " << (int)pVtab[s+1] << endl;
s = s + sizeof(Base2)/4;
cout << "[" << s << "] Base3::_vptr->"<<endl;
pFun = (Fun)pVtab[s][0];
cout << " [0] "; pFun();
pFun = (Fun)pVtab[s][1];
cout << " [1] "; pFun();
pFun = (Fun)pVtab[s][2];
cout << " [2] "; pFun();
pFun = (Fun)pVtab[s][3];
cout << " [3] ";
cout<<pFun<<endl;
s++;
cout << "["<< s <<"] Base3.ibase3 = " << (int)pVtab[s] << endl;
s++;
cout << "["<< s <<"] Derive.iderive = " << (int)pVtab[s] << endl;
return 0;
}
內(nèi)存分布情況如下
Vtable for Base1
Base1::_ZTV5Base1: 5u entries
0 (int (*)(...))0
4 (int (*)(...))(& _ZTI5Base1)
8 (int (*)(...))Base1::f
12 (int (*)(...))Base1::g
16 (int (*)(...))Base1::h
Class Base1
size=8 align=4
base size=8 base align=4
Base1 (0x0x4d907a8) 0
vptr=((& Base1::_ZTV5Base1) + 8u)
Vtable for Base2
Base2::_ZTV5Base2: 5u entries
0 (int (*)(...))0
4 (int (*)(...))(& _ZTI5Base2)
8 (int (*)(...))Base2::f
12 (int (*)(...))Base2::g
16 (int (*)(...))Base2::h
Class Base2
size=8 align=4
base size=8 base align=4
Base2 (0x0x4d907e0) 0
vptr=((& Base2::_ZTV5Base2) + 8u)
Vtable for Base3
Base3::_ZTV5Base3: 5u entries
0 (int (*)(...))0
4 (int (*)(...))(& _ZTI5Base3)
8 (int (*)(...))Base3::f
12 (int (*)(...))Base3::g
16 (int (*)(...))Base3::h
Class Base3
size=8 align=4
base size=8 base align=4
Base3 (0x0x4d90818) 0
vptr=((& Base3::_ZTV5Base3) + 8u)
Vtable for Derive
Derive::_ZTV6Derive: 16u entries
0 (int (*)(...))0
4 (int (*)(...))(& _ZTI6Derive)
8 (int (*)(...))Derive::f
12 (int (*)(...))Base1::g
16 (int (*)(...))Base1::h
20 (int (*)(...))Derive::g1
24 (int (*)(...))-8
28 (int (*)(...))(& _ZTI6Derive)
32 (int (*)(...))Derive::_ZThn8_N6Derive1fEv
36 (int (*)(...))Base2::g
40 (int (*)(...))Base2::h
44 (int (*)(...))-16
48 (int (*)(...))(& _ZTI6Derive)
52 (int (*)(...))Derive::_ZThn16_N6Derive1fEv
56 (int (*)(...))Base3::g
60 (int (*)(...))Base3::h
Class Derive
size=28 align=4
base size=28 base align=4
Derive (0x0x4ddeb40) 0
vptr=((& Derive::_ZTV6Derive) + 8u)
Base1 (0x0x4d90850) 0
primary-for Derive (0x0x4ddeb40)
Base2 (0x0x4d90888) 8
vptr=((& Derive::_ZTV6Derive) + 32u)
Base3 (0x0x4d908c0) 16
vptr=((& Derive::_ZTV6Derive) + 52u)
程序的輸出結(jié)果為
[0] Base1::_vptr->
[0] Derive::f()
[1] Base1::g()
[2] Base1::h()
[3] Derive::g1()
[4] 1
[1] Base1.ibase1 = 10
[2] Base2::_vptr->
[0] Derive::f()
[1] Base2::g()
[2] Base2::h()
[3] 1
[3] Base2.ibase2 = 20
[4] Base3::_vptr->
[0] Derive::f()
[1] Base3::g()
[2] Base3::h()
[3] 0
[5] Base3.ibase3 = 30
[6] Derive.iderive = 100
結(jié)論:
- 每個父類都有自己的虛表。
- 子類的成員函數(shù)被放到了第一個父類的表中材泄。
- 內(nèi)存布局中沮焕,其父類布局依次按聲明順序排列。
- 每個父類的虛表中的f()函數(shù)都被overwrite成了子類的f()拉宗。這樣做就是為了解決不同的父類類型的指針指向同一個子類實例峦树,而能夠調(diào)用到實際的函數(shù)。
多重繼承
#include <iostream>
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 : 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: 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;}
};
int main(){
typedef void(*Fun)(void);
int** pVtab = NULL;
Fun pFun = NULL;
D d;
pVtab = (int**)&d;
cout << "[0] D::B1::_vptr->" << endl;
pFun = (Fun)pVtab[0][0];
cout << " [0] "; pFun();
pFun = (Fun)pVtab[0][1];
cout << " [1] "; pFun();
pFun = (Fun)pVtab[0][2];
cout << " [2] "; pFun();
pFun = (Fun)pVtab[0][3];
cout << " [3] "; pFun();
pFun = (Fun)pVtab[0][4];
cout << " [4] "; pFun();
pFun = (Fun)pVtab[0][5];
cout << " [5] 0x" << pFun << endl;
cout << "[1] B::ib = " << (int)pVtab[1] << endl;
cout << "[2] B::cb = " << static_cast<char>((int)(pVtab[2]))<< endl;
cout << "[3] B1::ib1 = " << (int)pVtab[3] << endl;
cout << "[4] B1::cb1 = " << (char)(int)pVtab[4] << endl;
cout << "[5] D::B2::_vptr->" << endl;
pFun = (Fun)pVtab[5][0];
cout << " [0] "; pFun();
pFun = (Fun)pVtab[5][1];
cout << " [1] "; pFun();
pFun = (Fun)pVtab[5][2];
cout << " [2] "; pFun();
pFun = (Fun)pVtab[5][3];
cout << " [3] "; pFun();
pFun = (Fun)pVtab[5][4];
cout << " [4] 0x" << pFun << endl;
cout << "[6] B::ib = " << (int)pVtab[6] << endl;
cout << "[7] B::cb = " << (char)(int)pVtab[7] << endl;
cout << "[8] B2::ib2 = " << (int)pVtab[8] << endl;
cout << "[9] B2::cb2 = " << (char)(int)pVtab[9] << endl;
cout << "[10] D::id = " << (int)pVtab[10] << endl;
cout << "[11] D::cd = " << (char)(int)pVtab[11] << endl;
return 0;
}
內(nèi)存分布情況如下
Vtable for B
B::_ZTV1B: 4u entries
0 (int (*)(...))0
4 (int (*)(...))(& _ZTI1B)
8 (int (*)(...))B::f
12 (int (*)(...))B::Bf
Class B
size=12 align=4
base size=9 base align=4
B (0x0x4dc27a8) 0
vptr=((& B::_ZTV1B) + 8u)
Vtable for B1
B1::_ZTV2B1: 6u entries
0 (int (*)(...))0
4 (int (*)(...))(& _ZTI2B1)
8 (int (*)(...))B1::f
12 (int (*)(...))B::Bf
16 (int (*)(...))B1::f1
20 (int (*)(...))B1::Bf1
Class B1
size=20 align=4
base size=17 base align=4
B1 (0x0x4e09780) 0
vptr=((& B1::_ZTV2B1) + 8u)
B (0x0x4dc27e0) 0
primary-for B1 (0x0x4e09780)
Vtable for B2
B2::_ZTV2B2: 6u entries
0 (int (*)(...))0
4 (int (*)(...))(& _ZTI2B2)
8 (int (*)(...))B2::f
12 (int (*)(...))B::Bf
16 (int (*)(...))B2::f2
20 (int (*)(...))B2::Bf2
Class B2
size=20 align=4
base size=17 base align=4
B2 (0x0x4e09c40) 0
vptr=((& B2::_ZTV2B2) + 8u)
B (0x0x4dc2818) 0
primary-for B2 (0x0x4e09c40)
Vtable for D
D::_ZTV1D: 14u entries
0 (int (*)(...))0
4 (int (*)(...))(& _ZTI1D)
8 (int (*)(...))D::f
12 (int (*)(...))B::Bf
16 (int (*)(...))D::f1
20 (int (*)(...))B1::Bf1
24 (int (*)(...))D::f2
28 (int (*)(...))D::Df
32 (int (*)(...))-20
36 (int (*)(...))(& _ZTI1D)
40 (int (*)(...))D::_ZThn20_N1D1fEv
44 (int (*)(...))B::Bf
48 (int (*)(...))D::_ZThn20_N1D2f2Ev
52 (int (*)(...))B2::Bf2
Class D
size=48 align=4
base size=45 base align=4
D (0x0x4e27040) 0
vptr=((& D::_ZTV1D) + 8u)
B1 (0x0x4e27080) 0
primary-for D (0x0x4e27040)
B (0x0x4dc2850) 0
primary-for B1 (0x0x4e27080)
B2 (0x0x4e270c0) 20
vptr=((& D::_ZTV1D) + 40u)
B (0x0x4dc2888) 20
primary-for B2 (0x0x4e270c0)
輸出結(jié)果為
[0] D::B1::_vptr->
[0] D::f()
[1] B::Bf()
[2] D::f1()
[3] B1::Bf1()
[4] D::f2()
[5] 0x1
[1] B::ib = 0
[2] B::cb = B
[3] B1::ib1 = 11
[4] B1::cb1 = 1
[5] D::B2::_vptr->
[0] D::f()
[1] B::Bf()
[2] D::f2()
[3] B2::Bf2()
[4] 0x0
[6] B::ib = 0
[7] B::cb = B
[8] B2::ib2 = 12
[9] B2::cb2 = 2
[10] D::id = 100
[11] D::cd = D
運行結(jié)果為:
發(fā)現(xiàn)最頂端的父類B其成員變量和虛函數(shù)存在于B1和B2中簿废,并被D給繼承下去了空入。而在D中,其有B1和B2的實例族檬,于是B的成員在D的實例中存在兩份歪赢,一份是B1繼承而來的,另一份是B2繼承而來的单料,因此就會浪費內(nèi)存埋凯。
盡管我們可以通過明確指明調(diào)用路徑以消除二義性,但二義性的潛在性還沒有消除扫尖,我們可以通過虛繼承來使D類只擁有一個ib實體白对。
虛繼承
虛繼承解決了菱形繼承中最派生類擁有多個間接父類實例的情況。虛繼承的派生類的內(nèi)存布局與普通繼承很多不同换怖,主要體現(xiàn)在:
- 虛繼承的子類甩恼,如果本身定義了新的虛函數(shù),則編譯器為其生成一個虛函數(shù)指針(vptr)以及一張?zhí)摵瘮?shù)表沉颂。該vptr位于對象內(nèi)存最前面条摸。vs非虛繼承:直接擴展父類虛函數(shù)表。
- 虛繼承的子類也單獨保留了父類的vprt與虛函數(shù)表铸屉。這部分內(nèi)容接與子類內(nèi)容以一個四字節(jié)的0來分界钉蒲。
- 虛繼承的子類對象中,含有四字節(jié)的虛表指針偏移值彻坛。
簡單虛繼承
#include <iostream>
using namespace std;
class B
{
public:
int ib;
public:
B(int i=1) :ib(i){}
virtual void f() { cout << "B::f()" << endl; }
virtual void Bf() { cout << "B::Bf()" << endl; }
};
class B1 : virtual public B
{
public:
int ib1;
public:
B1(int i = 100 ) :ib1(i) {}
virtual void f() { cout << "B1::f()" << endl; }
virtual void f1() { cout << "B1::f1()" << endl; }
virtual void Bf1() { cout << "B1::Bf1()" << endl; }
};
int main(){
B1 b;
using Fun=void(*)();
Fun pFun = NULL;
int ** pvtable = (int**)&b;
for (int i=0; i<9; i++){
if (pvtable[0][i]==NULL) {
cout<<"here:"<<(int)pvtable[0][i]<<endl;
i+=4;
continue;
}
pFun = (Fun)pvtable[0][i];
pFun();
}
cout<<"size == "<<sizeof(b)<<endl;
cout<<"[1]:B1::ib1="<<(int)pvtable[1]<<endl;
cout<<"[3]:B::ib="<<(int)pvtable[3]<<endl;
cout<<"[2]:B::ib="<<(int)pvtable[2]<<endl;
// pFun = (Fun)pvtable[2];
// pFun();
// for (int i=1; pvtable[1][i]!=NULL; i++){
// pFun = (Fun)pvtable[1][i];
// pFun();
// }
return 0;
}
內(nèi)存布局
Vtable for B
B::_ZTV1B: 4u entries
0 (int (*)(...))0
4 (int (*)(...))(& _ZTI1B)
8 (int (*)(...))B::f
12 (int (*)(...))B::Bf
Class B
size=8 align=4
base size=8 base align=4
B (0x0x4f527a8) 0
vptr=((& B::_ZTV1B) + 8u)
Vtable for B1
B1::_ZTV2B1: 12u entries
0 8u
4 (int (*)(...))0
8 (int (*)(...))(& _ZTI2B1)
12 (int (*)(...))B1::f
16 (int (*)(...))B1::f1
20 (int (*)(...))B1::Bf1
24 0u
28 4294967288u
32 (int (*)(...))-8
36 (int (*)(...))(& _ZTI2B1)
40 (int (*)(...))B1::_ZTv0_n12_N2B11fEv
44 (int (*)(...))B::Bf
VTT for B1
B1::_ZTT2B1: 2u entries
0 ((& B1::_ZTV2B1) + 12u)
4 ((& B1::_ZTV2B1) + 40u)
Class B1
size=16 align=4
base size=8 base align=4
B1 (0x0x4f9a540) 0
vptridx=0u vptr=((& B1::_ZTV2B1) + 12u)
B (0x0x4f527e0) 8 virtual
vptridx=4u vbaseoffset=-12 vptr=((& B1::_ZTV2B1) + 40u)
程序運行結(jié)果
B1::f()
B1::f1()
B1::Bf1()
here:0
B::Bf()
size == 16
[1]:B1::ib1=100
[3]:B::ib=1
[2]:B::ib=4781096
這里的內(nèi)存分布比較奇怪顷啼,與vc++的結(jié)果不一樣踏枣。
但是這里size是16 說明有兩個指針;
多重虛繼承
菱形虛擬繼承下钙蒙,最派生類D類的對象模型又有不同的構(gòu)成了茵瀑。在D類對象的內(nèi)存構(gòu)成上,有以下幾點:
- 在D類對象內(nèi)存中仪搔,基類出現(xiàn)的順序是:先是B1(最左父類)瘾婿,然后是B2(次左父類)蜻牢,最后是B(虛祖父類)
- D類對象的數(shù)據(jù)成員id放在B類前面烤咧,兩部分數(shù)據(jù)依舊以0來分隔。
- 編譯器沒有為D類生成一個它自己的vptr抢呆,而是覆蓋并擴展了最左父類的虛基類表煮嫌,與簡單繼承的對象模型相同。
- 超類B的內(nèi)容放到了D類對象內(nèi)存布局的最后抱虐。