溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

C++之多態(tài)的示例分析

發(fā)布時(shí)間:2021-06-15 15:41:13 來(lái)源:億速云 閱讀:143 作者:小新 欄目:編程語(yǔ)言

小編給大家分享一下C++之多態(tài)的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

什么是多態(tài)?

顧名思義就是同一個(gè)事物在不同場(chǎng)景下的多種形態(tài)。

C++之多態(tài)的示例分析 

下面會(huì)具體的詳細(xì)的介紹。

靜態(tài)多態(tài)

我們以前說(shuō)過(guò)的函數(shù)重載就是一個(gè)簡(jiǎn)單的靜態(tài)多態(tài)

int Add(int left, int right)
{
 return left + right;
}
double Add(double left, int right)
{
 return left + right;
}

int main()
{
 Add(10, 20);
 //Add(10.0, 20.0); //這是一個(gè)問(wèn)題代碼
 Add(10.0,20); //正常代碼
 return 0;
}

C++之多態(tài)的示例分析 

可以看出來(lái),靜態(tài)多態(tài)是編譯器在編譯期間完成的,編譯器會(huì)根據(jù)實(shí)參類(lèi)型來(lái)選擇調(diào)用合適的函數(shù),如果有合適的函數(shù)可以調(diào)用就調(diào),沒(méi)有的話(huà)就會(huì)發(fā)出警告或者報(bào)錯(cuò)。。。比較簡(jiǎn)單,不做多介紹。

C++之多態(tài)的示例分析

動(dòng)態(tài)多態(tài)

什么是動(dòng)態(tài)多態(tài)呢?
動(dòng)態(tài)多態(tài): 顯然這和靜態(tài)多態(tài)是一組反義詞,它是在程序運(yùn)行時(shí)根據(jù)基類(lèi)的引用(指針)指向的對(duì)象來(lái)確定自己具體該調(diào)用哪一個(gè)類(lèi)的虛函數(shù)。

我在西安臨潼上學(xué),我就以這邊的公交車(chē)舉個(gè)栗子?。?/p>

class TakeBus
{
public:
 void TakeBusToSubway()
 {
  cout << "go to Subway--->please take bus of 318" << endl;
 }
 void TakeBusToStation()
 {
  cout << "go to Station--->pelase Take Bus of 306 or 915" << endl;
 }
};
//知道了去哪要做什么車(chē)可不行,我們還得知道有沒(méi)有這個(gè)車(chē)
class Bus
{
public:
 virtual void TakeBusToSomewhere(TakeBus& tb) = 0; //???為什么要等于0
};

class Subway:public Bus
{
public:
 virtual void TakeBusToSomewhere(TakeBus& tb)
 {
  tb.TakeBusToSubway();
 }
};
class Station :public Bus
{
public:
 virtual void TakeBusToSomewhere(TakeBus& tb)
 {
  tb.TakeBusToStation();
 }
};

int main()
{
 TakeBus tb;
 Bus* b = NULL;
 //假設(shè)有十輛公交車(chē),如果是奇數(shù)就是去地鐵口的,反之就是去火車(chē)站的
 for (int i = 1; i <= 10; ++i)
 {
  if ((rand() % i) & 1)
   b = new Subway;
  else
   b = new Station;
 }
 b->TakeBusToSomewhere(tb);
 delete b;
 return 0;
}

這就是一個(gè)簡(jiǎn)單的動(dòng)態(tài)多態(tài)的例子,它是在程序運(yùn)行時(shí)根據(jù)條件去選擇調(diào)用哪一個(gè)函數(shù)。
而且,從上面的例子我們還發(fā)現(xiàn)了我在每一個(gè)函數(shù)前都加了virtual這個(gè)虛擬關(guān)鍵字,想想為什么?如果不加會(huì)不會(huì)構(gòu)成多態(tài)呢?
干想不如上機(jī)實(shí)踐:

class Base
{
public:
 virtual void Funtest1(int i)
 {
  cout << "Base::Funtest1()" << endl;
 }
 void Funtest2(int i)
 {
  cout << "Base::Funtest2()" << endl;
 }
};
class Drived :public Base
{
 virtual void Funtest1(int i)
 {
  cout << "Drived::Fubtest1()" << endl;
 }
 virtual void Funtest2(int i)
 {
  cout << "Drived::Fubtest2()" << endl;
 }
 void Funtest2(int i)
 {
  cout << "Drived::Fubtest2()" << endl;
 }
};
void TestVirtual(Base& b)
{
 b.Funtest1(1);
 b.Funtest2(2);
}
int main()
{
 Base b;
 Drived d;
 TestVirtual(b);
 TestVirtual(d);
 return 0;
}

C++之多態(tài)的示例分析 

在調(diào)用FuncTest2的時(shí)候我們看出來(lái)他并沒(méi)有給我們調(diào)用派生類(lèi)的函數(shù),因此我們可以對(duì)動(dòng)態(tài)多態(tài)的實(shí)現(xiàn)做個(gè)總結(jié)。

動(dòng)態(tài)多態(tài)的條件:

●基類(lèi)中必須包含虛函數(shù),并且派生類(lèi)中一定要對(duì)基類(lèi)中的虛函數(shù)進(jìn)行重寫(xiě)。
●通過(guò)基類(lèi)對(duì)象的指針或者引用調(diào)用虛函數(shù)。

重寫(xiě) :

(a)基類(lèi)中將被重寫(xiě)的函數(shù)必須為虛函數(shù)(上面的檢測(cè)用例已經(jīng)證實(shí)過(guò)了)
(b)基類(lèi)和派生類(lèi)中虛函數(shù)的原型必須保持一致(返回值類(lèi)型,函數(shù)名稱(chēng)以及參數(shù)列表),協(xié)變和析構(gòu)函數(shù)(基類(lèi)和派生類(lèi)的析構(gòu)函數(shù)是不一樣的)除外
(c)訪(fǎng)問(wèn)限定符可以不同
那么問(wèn)題又來(lái)了,什么是協(xié)變?
協(xié)變:基類(lèi)(或者派生類(lèi))的虛函數(shù)返回基類(lèi)(派生類(lèi))的指針(引用)
總結(jié)一道面試題:那些函數(shù)不能定義為虛函數(shù)?
經(jīng)檢驗(yàn)下面的幾個(gè)函數(shù)都不能定義為虛函數(shù):
1)友元函數(shù),它不是類(lèi)的成員函數(shù)
2)全局函數(shù)
3)靜態(tài)成員函數(shù),它沒(méi)有this指針
3)構(gòu)造函數(shù),拷貝構(gòu)造函數(shù),以及賦值運(yùn)算符重載(可以但是一般不建議作為虛函數(shù))

抽象類(lèi):

在前面公交車(chē)的例子上我提了一個(gè)問(wèn)題:

class Bus
{
public:
 virtual void TakeBusToSomewhere(TakeBus& tb) = 0; //???為什么要等于0
};

在成員函數(shù)(必須為虛函數(shù))的形參列表后面寫(xiě)上=0,則成員函數(shù)為純虛函數(shù)。包含純虛函數(shù)的類(lèi)叫做抽象類(lèi)(也叫接口類(lèi)),抽象類(lèi)不能實(shí)例化出對(duì)象。純虛函數(shù)在派生類(lèi)中重新定義以后,派生類(lèi)才能實(shí)例化出對(duì)象。純虛函數(shù)是一定要被繼承的,否則它存在沒(méi)有任何意義。

C++之多態(tài)的示例分析

多態(tài)調(diào)用原理

class Base
{
public:
 virtual void Funtest1(int i)
 {
  cout << "Base::Funtest1()" << endl;
 }
 virtual void Funtest2(int i)
 {
  cout << "Base::Funtest2()" << endl;
 }
 int _data;
};

int main()
{
 cout << sizeof(Base) << endl;
 Base b;
 b._data = 10;
 return 0;
}

C++之多態(tài)的示例分析 

8?不知道大家有沒(méi)有問(wèn)題,反正我是有疑惑了。以前在對(duì)象模型(https://blog.csdn.net/qq_39412582/article/details/80808754)時(shí)我提到過(guò)怎么來(lái)求一個(gè)類(lèi)的大小。按照那個(gè)方法,這里應(yīng)該是4才對(duì)啊,為什么會(huì)是8呢?

通過(guò)觀察。我們發(fā)現(xiàn)這個(gè)例子里面和以前不一樣,類(lèi)成員函數(shù)變成了虛函數(shù),這是不是引起類(lèi)大小變化的原因呢?
我們假設(shè)就是這樣,然后看看內(nèi)存里是怎么存儲(chǔ)的呢?

C++之多態(tài)的示例分析 

可以看到它在內(nèi)存里多了四個(gè)字節(jié),那這四個(gè)字節(jié)的內(nèi)容到底是什么呢?

C++之多態(tài)的示例分析

是不是有點(diǎn)看不懂,我們假設(shè)它是一個(gè)地址去看地址里存的東西的時(shí)候發(fā)現(xiàn)它存的是兩個(gè)地址。
我假設(shè)它是虛函數(shù)的地址,我們來(lái)驗(yàn)證一下:

typedef void (__stdcall *PVFT)(); //函數(shù)指針
int main()
{
 cout << sizeof(Base) << endl;
 Base b;
 b._data = 10;
 PVFT* pVFT = (PVFT*)(*((int*)&b));
 while (*pVFT)
 {
  (*pVFT)();
  pVFT+=1;
 }
 return 0;
}

C++之多態(tài)的示例分析 

結(jié)果好像和我們的猜想一樣,是一件開(kāi)心的事。然后我給一張圖總結(jié)一下:

C++之多態(tài)的示例分析 

在反匯編中我們還可以看到,如果含有虛函數(shù)的類(lèi)中沒(méi)有定義構(gòu)造函數(shù),編譯器會(huì)自動(dòng)合成一個(gè)構(gòu)造函數(shù)

C++之多態(tài)的示例分析

C++之多態(tài)的示例分析

對(duì)于派生類(lèi)的東西我給個(gè)鏈接仔細(xì)看,人家總結(jié)的超級(jí)贊,我偷個(gè)懶就不寫(xiě)了,老鐵們包容下啊。

派生類(lèi)虛表:

1.先將基類(lèi)的虛表中的內(nèi)容拷貝一份
2.如果派生類(lèi)對(duì)基類(lèi)中的虛函數(shù)進(jìn)行重寫(xiě),使用派生類(lèi)的虛函數(shù)替換相同偏移量位置的基類(lèi)虛函數(shù)
3.如果派生類(lèi)中新增加自己的虛函數(shù),按照其在派生類(lèi)中的聲明次序,放在上述虛函數(shù)之后

https://coolshell.cn/articles/12176.html

多態(tài)缺陷

●降低了程序運(yùn)行效率(多態(tài)需要去找虛表的地址)
●空間浪費(fèi)

以上是“C++之多態(tài)的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

c++
AI