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

最佳實(shí)踐指南
使用
override關(guān)鍵字明確重寫(xiě)意圖基類(lèi)析構(gòu)函數(shù)必須聲明為
virtual接口類(lèi)使用純虛函數(shù)
性能考量:虛函數(shù)調(diào)用比普通函數(shù)多一次指針解引用
虛函數(shù)重寫(xiě)
協(xié)變
派?類(lèi)重寫(xiě)基類(lèi)虛函數(shù)時(shí),與基類(lèi)虛函數(shù)返回值類(lèi)型不同。即基類(lèi)虛函數(shù)返回基類(lèi)對(duì)象的指針或者引?,派?類(lèi)虛函數(shù)返回派?類(lèi)對(duì)象的指針或者引?時(shí),稱(chēng)為協(xié)變。核心概念
- 基類(lèi)虛函數(shù)返回基類(lèi)類(lèi)型指針 / 引用。
- 派生類(lèi)重寫(xiě)函數(shù)返回派生類(lèi)類(lèi)型指針 / 引用。
- 編譯器會(huì)自動(dòng)處理類(lèi)型轉(zhuǎn)換,確保調(diào)用的一致性。
協(xié)變的條件
- 基類(lèi)函數(shù)必須為虛函數(shù)。
- 返回類(lèi)型必須是指針或引用(不能是值類(lèi)型)。
- 派生類(lèi)返回類(lèi)型必須是基類(lèi)返回類(lèi)型的公有派生類(lèi)。
- 函數(shù)簽名的其他部分(參數(shù)、常量性)必須完全相同。
析構(gòu)函數(shù)的重寫(xiě)
基類(lèi)的析構(gòu)函數(shù)為虛函數(shù),此時(shí)派?類(lèi)析構(gòu)函數(shù)只要定義,?論是否加virtual關(guān)鍵字,都與基類(lèi)的析構(gòu)函數(shù)構(gòu)成重寫(xiě),雖然基類(lèi)與派?類(lèi)析構(gòu)函數(shù)名字不同看起來(lái)不符合重寫(xiě)的規(guī)則,實(shí)際上編譯器對(duì)析構(gòu)函數(shù)的名稱(chēng)做了特殊處理,編譯后析構(gòu)函數(shù)的名稱(chēng)統(tǒng)?處理成destructor,所以基類(lèi)的析構(gòu)函數(shù)加了vialtual修飾,派?類(lèi)的析構(gòu)函數(shù)就構(gòu)成重寫(xiě)。
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時(shí)只調(diào)?的A的析構(gòu)函數(shù),沒(méi)有調(diào)?B的析構(gòu)函數(shù),就會(huì)導(dǎo)致內(nèi)存泄漏問(wèn)題,因?yàn)?~B()中在釋放資源。
只有派?類(lèi)Student的析構(gòu)函數(shù)重寫(xiě)了Person的析構(gòu)函數(shù),下?的delete對(duì)象調(diào)?析構(gòu)函數(shù),才能構(gòu)成多態(tài),才能保證p1和p2指向的對(duì)象正確的調(diào)?析構(gòu)函數(shù)。
override和final關(guān)鍵字
- override 關(guān)鍵字
作用
明確標(biāo)記一個(gè)函數(shù)是重寫(xiě)基類(lèi)的虛函數(shù),編譯器會(huì)檢查以下條件:
基類(lèi)中是否存在同名虛函數(shù)。
基類(lèi)和派生類(lèi)的函數(shù)簽名(參數(shù)、常量性、引用限定符)是否嚴(yán)格一致。
返回類(lèi)型是否符合協(xié)變規(guī)則(如果適用)。 - final 關(guān)鍵字
作用
final 有兩種用法:
修飾虛函數(shù):禁止派生類(lèi)重寫(xiě)該虛函數(shù)。
修飾類(lèi):禁止該類(lèi)被繼承。
純虛函數(shù)和抽象類(lèi)
在虛函數(shù)的后?寫(xiě)上=0,則這個(gè)函數(shù)為純虛函數(shù),純虛函數(shù)不需要定義實(shí)現(xiàn)(實(shí)現(xiàn)沒(méi)啥意義因?yàn)橐慌?類(lèi)重寫(xiě),但是語(yǔ)法上可以實(shí)現(xiàn)),只要聲明即可。包含純虛函數(shù)的類(lèi)叫做抽象類(lèi),抽象類(lèi)不能實(shí)例化出對(duì)象,如果派?類(lèi)繼承后不重寫(xiě)純虛函數(shù),那么派?類(lèi)也是抽象類(lèi)。純虛函數(shù)某種程度上強(qiáng)制了派?類(lèi)重寫(xiě)虛函數(shù),因?yàn)椴恢貙?xiě)實(shí)例化不出對(duì)象
- 純虛函數(shù):在基類(lèi)中聲明為
virtual,并且沒(méi)有實(shí)現(xiàn)的函數(shù),形式為virtual void func() = 0;。 - 抽象類(lèi):包含純虛函數(shù)的類(lèi)稱(chēng)為抽象類(lèi),不能實(shí)例化對(duì)象,只能作為基類(lèi)被派生。
- 示例:
class Shape { public: virtual void draw() = 0; // 純虛函數(shù) }; class Circle : public Shape { public: void draw() override { cout << "Drawing Circle" << endl; } };Shape是一個(gè)抽象類(lèi),Circle是派生類(lèi),重寫(xiě)了draw()函數(shù)。- 多態(tài)的使用:
Shape* shape = new Circle(); shape->draw(); // 輸出:Drawing Circle
多態(tài)的原理
多態(tài)是如何實(shí)現(xiàn)的
在C++中,多態(tài)主要通過(guò)虛函數(shù)(virtual functions)和繼承機(jī)制來(lái)實(shí)現(xiàn)。以下是多態(tài)實(shí)現(xiàn)的詳細(xì)過(guò)程和原理:
1. 虛函數(shù)表(vtable)
- 概念:每個(gè)包含虛函數(shù)的類(lèi)都會(huì)有一個(gè)虛函數(shù)表(vtable),它是一個(gè)存儲(chǔ)虛函數(shù)地址的數(shù)組。當(dāng)創(chuàng)建一個(gè)類(lèi)的對(duì)象時(shí),對(duì)象會(huì)包含一個(gè)指向其類(lèi)的虛函數(shù)表的指針(vptr)。
- 作用:vtable是實(shí)現(xiàn)多態(tài)的關(guān)鍵。當(dāng)通過(guò)基類(lèi)指針或引用調(diào)用虛函數(shù)時(shí),程序會(huì)通過(guò)vptr找到對(duì)應(yīng)的vtable,然后通過(guò)vtable找到實(shí)際的虛函數(shù)地址。
虛函數(shù)表知識(shí)要點(diǎn)
- 基類(lèi)對(duì)象的虛函數(shù)表中存放基類(lèi)所有虛函數(shù)的地址。同類(lèi)型的對(duì)象共?同?張?zhí)摫恚煌?lèi)型的對(duì)象各?有獨(dú)?的虛表,所以基類(lèi)和派?類(lèi)有各?獨(dú)?的虛表。
- 派?類(lèi)由兩部分構(gòu)成,繼承下來(lái)的基類(lèi)和??的成員,?般情況下,繼承下來(lái)的基類(lèi)中有虛函數(shù)表指針,??就不會(huì)再?成虛函數(shù)表指針。但是要注意的這?繼承下來(lái)的基類(lèi)部分虛函數(shù)表指針和基類(lèi)對(duì)象的虛函數(shù)表指針不是同?個(gè),就像基類(lèi)對(duì)象的成員和派?對(duì)象中的基類(lèi)對(duì)象成員也獨(dú)?的。
- 派?類(lèi)中重寫(xiě)的基類(lèi)的虛函數(shù),派?類(lèi)的虛函數(shù)表中對(duì)應(yīng)的虛函數(shù)就會(huì)被覆蓋成派?類(lèi)重寫(xiě)的虛函數(shù)地址。
- 派?類(lèi)的虛函數(shù)表中包含,(1)基類(lèi)的虛函數(shù)地址,(2)派?類(lèi)重寫(xiě)的虛函數(shù)地址完成覆蓋,派?類(lèi)??的虛函數(shù)地址三個(gè)部分。
- 虛函數(shù)表本質(zhì)是?個(gè)存虛函數(shù)指針的指針數(shù)組,?般情況這個(gè)數(shù)組最后?放了?個(gè)0x00000000標(biāo)記。(此條取決于編譯器)
- 虛函數(shù)存儲(chǔ)的位置(vs下為常量區(qū))
可以自行驗(yàn)證:
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;
}
//通過(guò)比較虛表地址,找到其最近的內(nèi)存片段來(lái)確定
2. 虛函數(shù)的聲明
- 在類(lèi)中使用
virtual關(guān)鍵字聲明函數(shù)為虛函數(shù)。
Base為基類(lèi),Derived為繼承類(lèi)。class Base { public: virtual void show() { cout << "Base show()" << endl; } }; class Derived : public Base { public: void show() override { // override表示重寫(xiě)基類(lèi)的虛函數(shù) cout << "Derived show()" << endl; } };Base類(lèi)的show()函數(shù)被聲明為虛函數(shù),Derived類(lèi)重寫(xiě)了這個(gè)虛函數(shù)。
3. 多態(tài)的實(shí)現(xiàn)過(guò)程
- 創(chuàng)建對(duì)象:
Derived d;
- 當(dāng)創(chuàng)建
Derived類(lèi)的對(duì)象d時(shí),對(duì)象中會(huì)包含一個(gè)vptr,指向Derived類(lèi)的vtable。 Derived類(lèi)的vtable中存儲(chǔ)了Derived類(lèi)重寫(xiě)的虛函數(shù)show()的地址。
- 當(dāng)創(chuàng)建
- 通過(guò)基類(lèi)指針或引用調(diào)用虛函數(shù):
Base* ptr = &d; ptr->show();
ptr是一個(gè)指向Base類(lèi)的指針,但它指向的是Derived類(lèi)的對(duì)象。- 當(dāng)調(diào)用
ptr->show()時(shí),程序會(huì)通過(guò)ptr找到對(duì)象的vptr,然后通過(guò)vptr找到Derived類(lèi)的vtable,最終調(diào)用Derived類(lèi)的show()函數(shù)。 - 這就是多態(tài)的運(yùn)行時(shí)綁定(動(dòng)態(tài)綁定)過(guò)程。
動(dòng)態(tài)綁定與靜態(tài)綁定
• 對(duì)不滿(mǎn)?多態(tài)條件(指針或者引?+調(diào)?虛函數(shù))的函數(shù)調(diào)?是在編譯時(shí)綁定,也就是編譯時(shí)確定調(diào)?函數(shù)的地址,叫做靜態(tài)綁定。
• 滿(mǎn)?多態(tài)條件的函數(shù)調(diào)?是在運(yùn)?時(shí)綁定,也就是在運(yùn)?時(shí)到指向?qū)ο蟮奶摵瘮?shù)表中找到調(diào)?函數(shù)的地址,也就做動(dòng)態(tài)綁定。
總結(jié)
到此這篇關(guān)于C++三大核心特性之多態(tài)詳解的文章就介紹到這了,更多相關(guān)C++多態(tài)詳解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實(shí)現(xiàn)統(tǒng)計(jì)代碼運(yùn)行時(shí)間的示例詳解
這篇文章主要為大家詳細(xì)介紹了C++一個(gè)有趣的小項(xiàng)目——統(tǒng)計(jì)代碼運(yùn)行時(shí)間,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-05-05
C/C++實(shí)現(xiàn)對(duì)STORM運(yùn)行信息查看及控制的方法
這篇文章主要介紹了C/C++實(shí)現(xiàn)對(duì)STORM運(yùn)行信息查看及控制的方法,需要的朋友可以參考下2014-07-07
c++中l(wèi)og4cplus日志庫(kù)使用的基本步驟和示例代碼
這篇文章主要給大家介紹了關(guān)于c++中l(wèi)og4cplus日志庫(kù)使用的相關(guān)資料,log4cplus是一款開(kāi)源的c++日志庫(kù),具有線(xiàn)程安全,靈活,以及多粒度控制的特點(diǎn),log4cplus可以將日志按照優(yōu)先級(jí)進(jìn)行劃分,使其可以面向程序的調(diào)試,運(yùn)行,測(cè)試,后期維護(hù)等軟件全生命周期,需要的朋友可以參考下2024-06-06
C語(yǔ)言實(shí)現(xiàn)順序循環(huán)隊(duì)列實(shí)例
大家好,本篇文章主要講的是C語(yǔ)言實(shí)現(xiàn)順序循環(huán)隊(duì)列實(shí)例,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話(huà)記得收藏一下2022-02-02
C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的聊天室功能
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的聊天室功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06
c++中關(guān)系運(yùn)算符重載的實(shí)現(xiàn)示例
C++中關(guān)系運(yùn)算符重載允許自定義類(lèi)型使用關(guān)系運(yùn)算符,重載規(guī)則包括可重載的運(yùn)算符、重載方式、返回值類(lèi)型、對(duì)稱(chēng)性、const修飾等,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2026-02-02
C++中set/multiset容器詳解(附測(cè)試用例與結(jié)果圖)
set/multiset屬于關(guān)聯(lián)式容器,底層結(jié)構(gòu)是用二叉樹(shù)實(shí)現(xiàn),下面這篇文章主要給大家介紹了關(guān)于C++中set/multiset容器的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02
wchar_t,char,string,wstring之間的相互轉(zhuǎn)換
以下是對(duì)wchar_t,char,string,wstring之間的相互轉(zhuǎn)換進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2013-09-09

