C++三大核心特性之多態(tài)詳解
什么是多態(tài)?
多態(tài)(Polymorphism)是面向?qū)ο缶幊痰娜蠛诵奶匦灾唬ǚ庋b、繼承、多態(tài)),源于希臘語"多種形態(tài)"。在C++中,它允許我們使用統(tǒng)一的接口處理不同類型的對象,顯著提高了代碼的靈活性和可擴展性。
核心概念
- 同一接口,多種形態(tài)
不同的對象可以通過相同的方法名調(diào)用,但實際執(zhí)行的邏輯由對象自身的類決定。 - 解耦調(diào)用與實現(xiàn)
調(diào)用者只需關(guān)注接口(方法名和參數(shù)),無需關(guān)心具體實現(xiàn),提高代碼的可擴展性和可維護性。
多態(tài)的定義及實現(xiàn)
多態(tài)的構(gòu)成條件
多態(tài)是?個繼承關(guān)系的下的類對象,去調(diào)?同?函數(shù),產(chǎn)?了不同的?為。
實現(xiàn)多態(tài)還有兩個必須重要條件:
必須是基類的指針或者引?調(diào)?虛函數(shù)
被調(diào)?的函數(shù)必須是虛函數(shù),并且完成了虛函數(shù)重寫/覆蓋。說明:要實現(xiàn)多態(tài)效果,第?必須是基類的指針或引?,因為只有基類的指針或引?才能既指向基類 對象?指向派?類對象;第?派?類必須對基類的虛函數(shù)完成重寫/覆蓋,重寫或者覆蓋了,基類和派 ?類之間才能有不同的函數(shù),多態(tài)的不同形態(tài)效果才能達到。
虛函數(shù)
類成員函數(shù)前?加virtual修飾,那么這個成員函數(shù)被稱為虛函數(shù)。注意?成員函數(shù)不能加virtual修飾。
class A
{
public:
virtual void() {}
};
虛函數(shù)的重寫/覆蓋
虛函數(shù)的重寫/覆蓋:派?類中有?個跟基類完全相同的虛函數(shù)(即派?類虛函數(shù)與基類虛函數(shù)的返回值類型、函數(shù)名字、參數(shù)列表完全相同),稱派?類的虛函數(shù)重寫了基類的虛函數(shù)。
注意:在重寫基類虛函數(shù)時,派?類的虛函數(shù)在不加virtual關(guān)鍵字時,也可以構(gòu)成重寫(因為繼承后基類的虛函數(shù)被繼承下來了在派?類依舊保持虛函數(shù)屬性)
關(guān)鍵技術(shù)原理
虛函數(shù)表(vtable)
- 每個包含虛函數(shù)的類自動生成虛函數(shù)表
- 存儲該類所有虛函數(shù)的地址
- 創(chuàng)建對象時隱式添加
vptr指針指向vtable
動態(tài)綁定過程

最佳實踐指南
使用
override關(guān)鍵字明確重寫意圖基類析構(gòu)函數(shù)必須聲明為
virtual接口類使用純虛函數(shù)
性能考量:虛函數(shù)調(diào)用比普通函數(shù)多一次指針解引用
虛函數(shù)重寫
協(xié)變
派?類重寫基類虛函數(shù)時,與基類虛函數(shù)返回值類型不同。即基類虛函數(shù)返回基類對象的指針或者引?,派?類虛函數(shù)返回派?類對象的指針或者引?時,稱為協(xié)變。核心概念
- 基類虛函數(shù)返回基類類型指針 / 引用。
- 派生類重寫函數(shù)返回派生類類型指針 / 引用。
- 編譯器會自動處理類型轉(zhuǎn)換,確保調(diào)用的一致性。
協(xié)變的條件
- 基類函數(shù)必須為虛函數(shù)。
- 返回類型必須是指針或引用(不能是值類型)。
- 派生類返回類型必須是基類返回類型的公有派生類。
- 函數(shù)簽名的其他部分(參數(shù)、常量性)必須完全相同。
析構(gòu)函數(shù)的重寫
基類的析構(gòu)函數(shù)為虛函數(shù),此時派?類析構(gòu)函數(shù)只要定義,?論是否加virtual關(guān)鍵字,都與基類的析構(gòu)函數(shù)構(gòu)成重寫,雖然基類與派?類析構(gòu)函數(shù)名字不同看起來不符合重寫的規(guī)則,實際上編譯器對析構(gòu)函數(shù)的名稱做了特殊處理,編譯后析構(gòu)函數(shù)的名稱統(tǒng)?處理成destructor,所以基類的析構(gòu)函數(shù)加了vialtual修飾,派?類的析構(gòu)函數(shù)就構(gòu)成重寫。
class A
{
public:
virtual ~A()
{
cout << "~A()" << endl;
}
};
class B : public A {
public:
~B()
{
cout << "~B()->delete:"<<_p<< endl;
delete _p;
}
protected:
int* _p = new int[10];
};
int main()
{
A* p1 = new A;
A* p2 = new B;
delete p1;
delete p2;
return 0;
}
如果~A(),不加virtual,那么delete p2時只調(diào)?的A的析構(gòu)函數(shù),沒有調(diào)?B的析構(gòu)函數(shù),就會導(dǎo)致內(nèi)存泄漏問題,因為 ~B()中在釋放資源。
只有派?類Student的析構(gòu)函數(shù)重寫了Person的析構(gòu)函數(shù),下?的delete對象調(diào)?析構(gòu)函數(shù),才能構(gòu)成多態(tài),才能保證p1和p2指向的對象正確的調(diào)?析構(gòu)函數(shù)。
override和final關(guān)鍵字
- override 關(guān)鍵字
作用
明確標記一個函數(shù)是重寫基類的虛函數(shù),編譯器會檢查以下條件:
基類中是否存在同名虛函數(shù)。
基類和派生類的函數(shù)簽名(參數(shù)、常量性、引用限定符)是否嚴格一致。
返回類型是否符合協(xié)變規(guī)則(如果適用)。 - final 關(guān)鍵字
作用
final 有兩種用法:
修飾虛函數(shù):禁止派生類重寫該虛函數(shù)。
修飾類:禁止該類被繼承。
純虛函數(shù)和抽象類
在虛函數(shù)的后?寫上=0,則這個函數(shù)為純虛函數(shù),純虛函數(shù)不需要定義實現(xiàn)(實現(xiàn)沒啥意義因為要被派?類重寫,但是語法上可以實現(xiàn)),只要聲明即可。包含純虛函數(shù)的類叫做抽象類,抽象類不能實例化出對象,如果派?類繼承后不重寫純虛函數(shù),那么派?類也是抽象類。純虛函數(shù)某種程度上強制了派?類重寫虛函數(shù),因為不重寫實例化不出對象
- 純虛函數(shù):在基類中聲明為
virtual,并且沒有實現(xiàn)的函數(shù),形式為virtual void func() = 0;。 - 抽象類:包含純虛函數(shù)的類稱為抽象類,不能實例化對象,只能作為基類被派生。
- 示例:
class Shape { public: virtual void draw() = 0; // 純虛函數(shù) }; class Circle : public Shape { public: void draw() override { cout << "Drawing Circle" << endl; } };Shape是一個抽象類,Circle是派生類,重寫了draw()函數(shù)。- 多態(tài)的使用:
Shape* shape = new Circle(); shape->draw(); // 輸出:Drawing Circle
多態(tài)的原理
多態(tài)是如何實現(xiàn)的
在C++中,多態(tài)主要通過虛函數(shù)(virtual functions)和繼承機制來實現(xiàn)。以下是多態(tài)實現(xiàn)的詳細過程和原理:
1. 虛函數(shù)表(vtable)
- 概念:每個包含虛函數(shù)的類都會有一個虛函數(shù)表(vtable),它是一個存儲虛函數(shù)地址的數(shù)組。當(dāng)創(chuàng)建一個類的對象時,對象會包含一個指向其類的虛函數(shù)表的指針(vptr)。
- 作用:vtable是實現(xiàn)多態(tài)的關(guān)鍵。當(dāng)通過基類指針或引用調(diào)用虛函數(shù)時,程序會通過vptr找到對應(yīng)的vtable,然后通過vtable找到實際的虛函數(shù)地址。
虛函數(shù)表知識要點
- 基類對象的虛函數(shù)表中存放基類所有虛函數(shù)的地址。同類型的對象共?同?張?zhí)摫?,不同類型的對象?有獨?的虛表,所以基類和派?類有各?獨?的虛表。
- 派?類由兩部分構(gòu)成,繼承下來的基類和??的成員,?般情況下,繼承下來的基類中有虛函數(shù)表指針,??就不會再?成虛函數(shù)表指針。但是要注意的這?繼承下來的基類部分虛函數(shù)表指針和基類對象的虛函數(shù)表指針不是同?個,就像基類對象的成員和派?對象中的基類對象成員也獨?的。
- 派?類中重寫的基類的虛函數(shù),派?類的虛函數(shù)表中對應(yīng)的虛函數(shù)就會被覆蓋成派?類重寫的虛函數(shù)地址。
- 派?類的虛函數(shù)表中包含,(1)基類的虛函數(shù)地址,(2)派?類重寫的虛函數(shù)地址完成覆蓋,派?類??的虛函數(shù)地址三個部分。
- 虛函數(shù)表本質(zhì)是?個存虛函數(shù)指針的指針數(shù)組,?般情況這個數(shù)組最后?放了?個0x00000000標記。(此條取決于編譯器)
- 虛函數(shù)存儲的位置(vs下為常量區(qū))
可以自行驗證:
class Base {
public:
virtual void func1() {}
protected:
int a = 1;
};
class Derive : public Base
{
public:
virtual void func1() {}
protected:
int b = 2;
};
int main()
{
int i = 0;
static int j = 1;
int* p1 = new int;
const char* p2 = "xxxxxxxx";
printf("棧:%p\n", &i);
printf("靜態(tài)區(qū):%p\n", &j);
printf("堆:%p\n", p1);
printf("常量區(qū):%p\n", p2);
Base b;
Base* p3 = &b;
printf("虛表地址:%p\n", *(int*)p3);
return 0;
}
//通過比較虛表地址,找到其最近的內(nèi)存片段來確定
2. 虛函數(shù)的聲明
- 在類中使用
virtual關(guān)鍵字聲明函數(shù)為虛函數(shù)。
Base為基類,Derived為繼承類。class Base { public: virtual void show() { cout << "Base show()" << endl; } }; class Derived : public Base { public: void show() override { // override表示重寫基類的虛函數(shù) cout << "Derived show()" << endl; } };Base類的show()函數(shù)被聲明為虛函數(shù),Derived類重寫了這個虛函數(shù)。
3. 多態(tài)的實現(xiàn)過程
- 創(chuàng)建對象:
Derived d;
- 當(dāng)創(chuàng)建
Derived類的對象d時,對象中會包含一個vptr,指向Derived類的vtable。 Derived類的vtable中存儲了Derived類重寫的虛函數(shù)show()的地址。
- 當(dāng)創(chuàng)建
- 通過基類指針或引用調(diào)用虛函數(shù):
Base* ptr = &d; ptr->show();
ptr是一個指向Base類的指針,但它指向的是Derived類的對象。- 當(dāng)調(diào)用
ptr->show()時,程序會通過ptr找到對象的vptr,然后通過vptr找到Derived類的vtable,最終調(diào)用Derived類的show()函數(shù)。 - 這就是多態(tài)的運行時綁定(動態(tài)綁定)過程。
動態(tài)綁定與靜態(tài)綁定
• 對不滿?多態(tài)條件(指針或者引?+調(diào)?虛函數(shù))的函數(shù)調(diào)?是在編譯時綁定,也就是編譯時確定調(diào)?函數(shù)的地址,叫做靜態(tài)綁定。
• 滿?多態(tài)條件的函數(shù)調(diào)?是在運?時綁定,也就是在運?時到指向?qū)ο蟮奶摵瘮?shù)表中找到調(diào)?函數(shù)的地址,也就做動態(tài)綁定。
總結(jié)
到此這篇關(guān)于C++三大核心特性之多態(tài)詳解的文章就介紹到這了,更多相關(guān)C++多態(tài)詳解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
c語言實現(xiàn)把文件中數(shù)據(jù)讀取并存到數(shù)組中
下面小編就為大家?guī)硪黄猚語言實現(xiàn)把文件中數(shù)據(jù)讀取并存到數(shù)組中。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-12-12
詳解vs2022創(chuàng)建及調(diào)用.lib的方法
這篇文章主要介紹了vs2022創(chuàng)建及調(diào)用.lib的方法,調(diào)用Lib的原則就是可以讓編譯器找到頭文件和庫文件的目錄,并正確引入,本文給大家詳細講解需要的朋友可以參考下2022-11-11
C++基于Floyd算法實現(xiàn)校園導(dǎo)航系統(tǒng)
這篇文章主要為大家詳細介紹了C++基于Floyd算法實現(xiàn)校園導(dǎo)航系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03
C語言關(guān)鍵字const和指針的結(jié)合使用
這篇文章主要介紹了C語言關(guān)鍵字const和指針的結(jié)合,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2020-02-02

