C++多态相关关问题及虚表剖析
關(guān)于C++多態(tài)的問題:(基于Visual Studio 2012編譯器)
?
一、多態(tài)引入
1、對象的類型:
(1)???靜態(tài)的類型:對象聲明時的類型,在編譯的時候確定
(2)???動態(tài)的類型:目前所指對象的類型,在程序運行時確定的
EG:
class Derived1:public Base
{};
?
class Derived2:public Base
{};
?
void FunTest()
{
???Derived1* pD1 = new Derived1;//對象pD1的靜態(tài)類型是Derived1, 動態(tài)類型是Derived1*
???Base* pB = pD1;//對象pB的靜態(tài)類型是Base*, 動態(tài)類型是Derived1*
???Derived2* pD2 = new Derived2;
???pB = pD2;//對象pB的靜態(tài)類型是Base*,動態(tài)類型是Derived2
}
?
2、多態(tài):
(1)???多態(tài)的概念:一詞最初來源于希臘語,意思是具有多種形式或形態(tài)的情形,在C++中主要體現(xiàn)在想不通的對象發(fā)送同一個消息,不同的對象會產(chǎn)生不同的行為。
(2)???多態(tài)的類型:
A:一種為編譯時的多態(tài),稱為靜態(tài)多態(tài)或靜態(tài)聯(lián)編。編譯時的多態(tài)性是指程序在編譯以前就確定的多態(tài)性,可以通過重載、泛型編程來實現(xiàn)。
B:另一種是運行時的多態(tài),也稱為靜態(tài)聯(lián)編,運行時的多態(tài)是指在程序運行中才可以確定的多態(tài)性,是通過繼承和虛函數(shù)實現(xiàn)的。
(3)???動態(tài)的多態(tài):
動態(tài)綁定(早綁定):在程序執(zhí)行期間(非編譯期)判斷所引用對象的實際類型,根據(jù)其實際類型調(diào)用相應(yīng)的方法。
使用virtual關(guān)鍵字修飾類的成員函數(shù)時,指明該函數(shù)為虛函數(shù),派生類需要重新實現(xiàn),編譯器將實現(xiàn)動態(tài)綁定。
class CWashRoom
{
public:
???void GoToManWashRoom ()
??? {
???cout<< "Man--->Please Left" <<endl;
??? }
???void GoToWomanWashRoom ()
??? {
???cout<< "Woman--->Please Right" <<endl ;
??? }
};
class CPerson
{
public:
???virtual void GoToWashRoom(CWashRoom & _washRoom ) = 0;//virtual修飾函數(shù)為虛函數(shù),在成員函數(shù)的形參后面寫上=0,則成員函數(shù)為純虛函數(shù)。包含純虛函數(shù)的類叫做抽象類(也叫接口類),抽象類不能實例化出對象。純虛函數(shù)在派生類中重新定義以后,派生類才能實例化出對象。
};
?
class CMan:public CPerson
{
public:
???virtual void GoToWashRoom(CWashRoom & _washRoom )//對基類函數(shù)的重寫,進(jìn)行動態(tài)綁定,實現(xiàn)不同的功能。
??? {
???_washRoom.GoToManWashRoom();
??? }
};
class CWoman:public CPerson
{
public:
???virtual void GoToWashRoom(CWashRoom & _washRoom )
??? {
???_washRoom.GoToWomanWashRoom ();
??? }
};
?????????????????? 注:必須是虛函數(shù),通過基類類型的引用或者指針調(diào)用虛函數(shù)
??????????????????????????? voidFunTest()
{
CWashRoom washRoom;
for ( int iIdx = 1 ; iIdx <= 10 ; ++iIdx)
{
CPerson* pPerson;
int iPerson = rand ()%iIdx;
if (iPerson&0x01)
{
pPerson = new CMan ;
}
else
{
pPerson = new CWoman ;
}
pPerson->GoToWashRoom (washRoom); //通過基類類型的指針調(diào)用在派生類中//進(jìn)行重寫的虛函數(shù),
delete pPerson;
pPerson = NULL ;
Sleep(1000 );
}
}
3、? ??繼承體系中同名成員函數(shù)的關(guān)系和區(qū)別
4、? 小結(jié)
1、派生類重寫基類的虛函數(shù)實現(xiàn)多態(tài),要求函數(shù)名、參數(shù)列表、返回值完全相同。(協(xié)變除外)
2、基類中定義了虛函數(shù),在派生類中該函數(shù)始終保持虛函數(shù)的特性。
3、只有類的非靜態(tài)成員函數(shù)才能定義為虛函數(shù),靜態(tài)成員函數(shù)不能定義為虛函數(shù)。
4、如果在類外定義虛函數(shù),只能在聲明函數(shù)時加virtual關(guān)鍵字,定義時不用加。
5、構(gòu)造函數(shù)不能定義為虛函數(shù),雖然可以將operator=定義為虛函數(shù),但最好不要這么做,使用時容易混淆
6、不要在構(gòu)造函數(shù)和析構(gòu)函數(shù)中調(diào)用虛函數(shù),在構(gòu)造函數(shù)和析構(gòu)函數(shù)中,對象是不完整的,沒有傳遞this指針,可能會出現(xiàn)未定義的行為。
7、最好將基類的析構(gòu)函數(shù)聲明為虛函數(shù)。(析構(gòu)函數(shù)比較特殊,因為派生類的析構(gòu)函數(shù)跟基類的析構(gòu)函數(shù)名稱不一樣,但是構(gòu)成覆蓋,這里編譯器做了特殊處理)
8、虛表是所有類對象實例共用的
?
?
5、? 虛表分析
(1)??????單繼承:
派生類中沒有重寫基類的虛函數(shù)
classBase
{
public:
??? Base()
??? {}
?
??? virtualvoid Test1()
??? {
??????? //data = 10;
??????? //cout<<this<<endl;
??????? //cout<<data<<endl;
??????? cout<<"BaseTest1()"<<endl;
??? }
?
??? virtualvoid Test2()
??? {
??????? cout<<"BaseTest2()"<<endl;
??? }
?
??? virtualvoid Test3()
??? {
??????? cout<<"BaseTest3()"<<endl;
??? }
?
??? int data;
};
?
classDerited:publicBase
{
public:
??? virtualvoid Test4()//復(fù)制一份基類虛函數(shù),如果在派生類中對基類虛函數(shù)進(jìn)行重寫,則用重寫的虛函數(shù)代替
??? {
??????? cout<<"DeritedTest4()"<<endl;
??? }
?
??? virtualvoid Test5()
??? {
??????? cout<<"DeritedTest5()"<<endl;
??? }
??? virtualvoid Test6()
??? {
??????? cout<<"Deritedtest6()"<<endl;
??? }
};
void FunTest2(Base& b)
{
??? b.Test2();
}
?
typedef void (*VFP)();
?
void printVpf()
{
??? Base b;
??? b.data1? = 1;
??? cout<<"Base::virtable of B:"<<endl;
??? VFP* vfp = (VFP *)*(int *)&b; //未傳遞this指針。
??????????????????????????????? ? //最好不要訪問類的成員變量
??? while(*vfp)
??? {
??????? (*vfp)();
??????? ++vfp;
??? }
??? cout<<endl;
?
??? Derited d;
??? d.data1 = 1;
??? d.data2 = 2;
??? cout<<"Base::virtable of D:"<<endl;
??? VFP* vfp1 = (VFP *)*(int *)&d;
?
??? while(*vfp1)
??? {
??????? (*vfp1)();
??????? ++vfp1;
??? }
??? cout<<endl;
}
在派生類中重寫基類的虛函數(shù)
?
classBase
{
public:
??? Base()
??? {}
?
??? virtualvoid Test1()
??? {
??????? //data = 10;
??????? //cout<<this<<endl;
??????? //cout<<data<<endl;
??????? cout<<"BaseTest1()"<<endl;
??? }
?
??? virtualvoid Test2()
??? {
??????? cout<<"BaseTest2()"<<endl;
??? }
?
??? virtualvoid Test3()
??? {
??????? cout<<"BaseTest3()"<<endl;
??? }
?
??? int data;
};
?
classDerited:publicBase
{
public:
??? virtualvoid Test1()//復(fù)制一份基類虛函數(shù),如果在派生類中對基類虛函數(shù)進(jìn)行重寫,則用重寫的虛函數(shù)代替
??? {
??????? cout<<"DeritedTest1()"<<endl;
??? }
?
??? virtualvoid Test3()
??? {
??????? cout<<"DeritedTest3()"<<endl;
??? }
??? virtualvoid Test4()
??? {
??????? cout<<"test4()"<<endl;
??? }
};
?
在重寫了基類虛函數(shù)的派生類虛表中先復(fù)制了一份基類的虛表,然后將在派生類中重寫的虛函數(shù)覆蓋掉基類的虛函數(shù),派生類自己的虛函數(shù)跟在復(fù)制下來的虛表后邊。通過基類的指針或引用調(diào)用虛函數(shù)時調(diào)用的是被覆蓋之后的虛表里的虛函數(shù)。
?
(2)??????多繼承:
class Base1
{
public:
?
? virtual void Test1()
? {
????? cout<<"Base1Test1()"<<endl;
? }
?
? virtual void Test2()
? {
????? cout<<"Base1Test2()"<<endl;
? }
?
? virtual void Test3()
? {
????? cout<<"Base1Test3()"<<endl;
? }
?
? int data1;
};
?
class Base2
{
public:
? virtual void Test4()
? {
?
????? cout<<"Base2Test4()"<<endl;
? }
?
? virtual void Test5()
? {
????? cout<<"Base2 Test5()"<<endl;
? }
?
? virtual void Test6()
? {
????? cout<<"Base2Test6()"<<endl;
? }
?
? int data2;
};
?
class Derited:public Base1,public Base2
{
public:
? Derited()
? {
????? data3 = 3;
? }
? virtual void Test2()
? {
????? cout<<"DeritedTest2()"<<endl;
? }
?
? virtual void Test4()
? {
????? cout<<"DeritedTest4()"<<endl;
? }
? virtual void Test7()
? {
????? cout<<"DeritedTest7()"<<endl;
? }
? int data3;
} ;
?
typedef void (* VFP)();
?
void printVpf()
{
? Derited d;
? d.data1 = 1;
? d.data2 = 2;
? d.data3 = 3;
? Base1 &b1 = d;
? VFP* vfp = (VFP *)*(int *)&b1;
? cout<<"&b1 ="<<&b1<<endl;????????????????? ?
? while(*vfp)
? {
????? (*vfp)();
????? ++vfp;
? }
? cout<<endl;
?
? Base2 &b2 = d;
? cout<<"&b2 ="<<&b2<<endl;
? cout<<"&b2 ="<<(Base2 *)((int)&b1+sizeof(Base1))<<endl;
? VFP* vfp1 = (VFP *)*(int *)&b2;
?
? while(*vfp1)
? {
????? (*vfp1)();
????? ++vfp1;
? }
? cout<<endl;
??
?
?
?
?
?
在多繼承中,基類的虛函數(shù)如果在子類中重寫,則用基類的指針或引用調(diào)用虛函數(shù),調(diào)用的是被覆蓋的虛表里的虛函數(shù)。
?
?
?
(3)??????菱形繼承
class Base
{
public:
? virtual void Test1()
? {
????? cout<<"Base::Test1()"<<endl;
? }
?
? int _data1;
};
class C1:public Base
{
public:
virtual void Test1()
? {
????? cout<<"C1::Test1()"<<endl;
? }
virtual void Test2()
? {
????? cout<<"C1::Test2()"<<endl;
? }
int _data2;
};
class C2:public Base
{
public:
virtual void Test1()
? {
????? cout<<"C2::Test1()"<<endl;
? }
virtual void Test3()
? {
????? cout<<"C2::Test3()"<<endl;
? }
int _data3;
};
class D:public C1,public C2
{
public:
virtual void Test1()
? {
????? cout<<"D::Test1()"<<endl;
? }
virtual void Test2()
? {
????? cout<<"D::Test2()"<<endl;
? }
virtual void Test3()
? {
????? cout<<"D::Test3()"<<endl;
? }
virtual void Test4()
? {
????? cout<<"D::Test4()"<<endl;
? }
int _data4;
};
typedef void(*VFP)();
void PrintVfp()
{
? D d;
? VFP* vfp1 = (VFP*)*(int *)&d;
? cout<<"D virtable:"<<endl;
? while(*vfp1)
? {
????? (*vfp1)();
????? ++vfp1;
? }
? cout<<endl;
?
? C2& c2 = d;
? VFP* vfp2 = (VFP*)*(int *)&c2;
? cout<<"C2 virtable:"<<endl;
? while(*vfp2)
? {
????? (*vfp2)();
????? ++vfp2;
? }
? cout<<endl;
?
? C1& c1 = d;
? VFP* vfp3 = (VFP*)*(int *)&c1;
? cout<<"C1 virtable:"<<endl;
? while(*vfp3)
? {
????? (*vfp3)();
????? ++vfp3;
? }
? cout<<endl;
Base& b = d;
}
int main()
{
? PrintVfp();
? D d;
? d.C1::_data1 = 0;
? d.C2::_data1 = 1;
? d._data2 = 2;
? d._data3 = 3;
? d._data4 = 4;
? system("pause");
? return 0;
}
?
在菱形繼承中C1的大小為C1的成員加上Base的成員大小再加上C1的虛表大小
D的大小為C1的大小加上C2的大小加上D自己的成員變量大小。在派生類中存儲了兩份Base類的對象,故訪問時存在二義性問題,由此引入了菱形虛擬繼承。
?
?
(4)??????菱形虛擬繼承:
class Base
{
public:
??? virtual void Test1()
??? {
??????? cout<<"Base::Test1()"<<endl;
??? }
?
??? int _data1;
???
};
class C1:virtual public Base
{
public:
??? virtual void Test1()
??????? {
??????????? cout<<"C1::Test1()"<<endl;
??????? }
??? virtual void Test2()
??????? {
??????????? cout<<"C1::Test3()"<<endl;
??????? }
?
?
int _data2;
};
class C2:virtual public Base
{
public:
??? virtual void Test1()
??????? {
??????????? cout<<"C2::Test2()"<<endl;
??????? }
??? virtual void Test3()
??????? {
??????????? cout<<"C2::Test4()"<<endl;
??????? }
??? int _data3;
};
class D:public C1,public C2
{
public:
??? virtual void Test1()
??????? {
??????????? cout<<"D::Test1()"<<endl;
??????? }
??? virtual void Test4()
??????? {
??????????? cout<<"D::Test4()"<<endl;
??????? }
??????? virtual void Test5()
??????? {
??????????? cout<<"DTest5()"<<endl;
??????? }
??? int _data4;
};
typedef void(*VFP)();
void PrintVfp()
{
??? D d;
??? VFP* vfp1 = (VFP*)*(int *)&d;
??? cout<<"D virtable:"<<endl;
??? while(*vfp1)
??? {
??????? (*vfp1)();
??????? ++vfp1;
??? }
??? cout<<endl;
?
??? C1 &c1 = d;
??? VFP* vfp2 = (VFP*)*(int *)&c1;
??? cout<<"C1 virtable:"<<endl;
??? while(*vfp2)
??? {
??????? (*vfp2)();
??????? ++vfp2;
??? }
??? cout<<endl;
?
??? Base& b = d;
??? VFP* vfp3 = (VFP*)*(int *)&b;
??? cout<<"Base virtable:"<<endl;
??? while(*vfp3)
??? {
??????? (*vfp3)();
??????? ++vfp3;
??? }
??? cout<<endl;
}
int main()
{?? cout<<sizeof(C1)<<endl;
??? cout<<sizeof(D)<<endl;
??? PrintVfp();
?
??? system("pause");
??? return 0;
}
?
C1的大小為20字節(jié):基類Base成員data1大小+派生類C1成員data2大小+偏移指針+虛表指針
D的大小為36個字節(jié)大小:C1成員大小+C1虛表指針+C1偏移指針+C2成員大小+C2虛表指針+C2偏移指針+派生類D成員大小+基類Base虛表+Base成員
?
(5)??????派生類帶有構(gòu)造函數(shù)+析構(gòu)函數(shù)、構(gòu)造函數(shù)、析構(gòu)函數(shù)其中一個成員函數(shù)的虛表分析:
?
class Base
{
public:
??? Base()
??????? :data1(1)
??? {}
??? ~Base()
??? {}
??? virtual void Test1()
??? {
??????? cout<<"BaseTest1()"<<endl;
??? }
?
??? virtual void Test2()
??? {
??????? cout<<"BaseTest2()"<<endl;
??? }
?
??? virtual void Test3()
??? {
??????? cout<<"BaseTest3()"<<endl;
??? }
?
??? int data1;
};
class Derited:virtual public Base
{
public:
??? Derited()
??? {}
??? ~Derited()
??? {}
??? virtual void Test1()
??? {
??????? cout<<"DeritedTest1()"<<endl;
??? }
??? virtual void Test3()
??? {
??????? cout<<"Deritedtest3()"<<endl;
??? }
??? virtual void Test4()
??? {
??????? cout<<"DeritedTest4()"<<endl;
??? }
??? int data2;
};
typedef void (*VFP)()
void printVpf()
{
??? Derited d;
??? d.data1 = 1;
??? d.data2 = 2;
??? cout<<"vir table ofD:"<<endl;
??? VFP* vfp1 = (VFP *)*(int *)&d;
?
??? while(*vfp1)
??? {
??????? (*vfp1)();
??????? ++vfp1;
??? }
??? cout<<endl;
??? Base& b = d;
??? cout<<"vir table ofB:"<<endl;
??? VFP* vfp2 = (VFP *)*(int *)&b;
??? while(*vfp2)
??? {
??????? (*vfp2)();
??????? ++vfp2;
??? }
??? cout<<endl;
}
int main()
{
??? printVpf();
??? system("pause");
??? return 0;
}
?
?
?
?
?
構(gòu)造函數(shù)在這個地方的作用:
(1)??????偏移量表地址的填寫
(2)??????調(diào)用基類構(gòu)造函數(shù),填寫基類虛表地址:
(3)??????填寫派生類虛表地址:
?
(4)??????重新填寫基類虛表地址
?
?
(5)??????插入派生類與基類分割0x000000
虛表:
?
此時派生類的大小為24字節(jié):派生類虛表指針+派生類偏移指針+派生類成員大小+基類虛表指針+基類成員大小+分割的 :0x00000000
?
?
總結(jié)
以上是生活随笔為你收集整理的C++多态相关关问题及虚表剖析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php 图片无法删除,php如何删除上
- 下一篇: html文件怎么生产vm页面,如何使用s