日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++多态相关关问题及虚表剖析

發(fā)布時間:2023/12/20 c/c++ 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++多态相关关问题及虚表剖析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

關(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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。