999精品在线视频,手机成人午夜在线视频,久久不卡国产精品无码,中日无码在线观看,成人av手机在线观看,日韩精品亚洲一区中文字幕,亚洲av无码人妻,四虎国产在线观看 ?

C++多態性的實現過程

2023-06-15 05:26:44李家宏孫慶英
無線互聯科技 2023年2期
關鍵詞:程序

李家宏 孫慶英

摘要:多態性特征是C++中最為重要的一個特征,熟練使用多態是學好C++的關鍵,而理解多態的實現機制及實現過程則是熟練使用多態的關鍵。文章在分析多態性基本屬性的基礎上,結合具體程序實例重點分析了動態多態的實現機制,并結合虛函數和聯編原理分析了動態多態的實現過程。

關鍵詞:C++;多態性;虛函數

中圖分類號:TP312.1? 文獻標志碼:A

0 引言

面向對象程序設計(Object Oriented Programming)是以對象為程序的基本單元,將數據和操作封裝其中,提高了軟件的重用性、靈活性和擴展性,C++是面向對象程序設計語言的主流之一。現實世界的諸多事物,包括一些抽象規則、計劃或事件都可以描述成對象。對象是由數據(描述事物的屬性)和作用于數據的操作(事物的行為)構成的一個獨立整體。

封裝、繼承和多態是面向對象設計的3大特點。封裝就是把客觀事物抽象得到的數據和行為封裝成一個整體,在C++中,實現數據和行為封裝的程序單元就叫類。封裝就是將代碼模塊化,實現了類內部對象的隱蔽。繼承是由已經存在的類創建新類的機制,體現在類的層次關系中,子類擁有父類中的數據和方法,子類繼承父類的同時可以修改和擴充自己的功能。多態是指父類的方法被子類重寫、可以各自產生自己的功能行為。封裝和繼承的目的是代碼的重用,多態就是實現接口重用,即“一個接口,多種方法”。相比封裝和繼承,多態因其復雜性、靈活性更難以掌握和理解。

1 多態的概念

多態(polymorphism)一詞最早來源于拉丁語poly(意為多)和morphos(意為形態),意指具有多種形式或形態。它反映了人們在思索解決問題的辦法時,對相似的問題的一種求解方法[1]。

多態性一詞最早來源于生物學,是指地球上所有生物,從食物鏈系統、物種水平、群體水平、基因水平等層次上所體現出的形態和狀態的多樣性[2]。多態性是指同樣的消息被不同類型的對象接收時會產生完全不同的行為,即根據操作環境的不同采用不同的處理方式,一組具有相同基本語義的方法能在同一接口下為不同的對象服務[3]。在C++中利用類繼承的層次關系來實現多態,通常是把具有通用功能的聲明存放在類層次高的地方,而把實現這一個功能的不同方法放在層次較低的類中,C++語言通過子類重定義父類函數來實現多態。

2 多態的分類

多態通常分為兩種:通用多態和特定多態,其中,通用多態又細分為參數多態和包含多態[4]。參數多態在C++中就是利用函數模板或類模板,給出的不同參數類型,得到不同的結果,實現一個具有多種形態的結構。包含多態在C++中的基礎就是虛函數,即同樣的操作可用于一個類型及其子類型。特定多態細分為重載多態和強制多態。重載多態在C++中就是函數重載和運算符重載,即同一個名(操作符、函數名)在不同的上下文中有不同的類型。強制多態,這里強制也稱為類型轉換,在C++中一般指基本類型轉換和自定義類型轉換,即在編譯的時候發生數據混合運算時,程序通過語義操作,改變操作對象的類型以符合運行時函數和操作符的要求。通用多態和特定多態的區別是:通用多態對工作的類型不加限制,允許不同類型的值執行相同的代碼,從語義上為相關聯性的類型,特定多態對有限的類型有效。不同類型的值可能要執行不同的代碼,從語義上為無關聯的類型。

3 多態的實現

3.1 類型兼容與函數重寫

C++中的繼承遵循了類型兼容性原則,即當子類以Public方式繼承父類時,將繼承父類的所有屬性和方法,因此,可以變相的理解成子類是一種特殊的父類,可以使用子類對象初始化父類,也可以使用父類的指針或引用來調用子類的對象。

在程序設計過程中,很多時候會出現這樣一種情況,子類繼承父類的A函數,但父類的A函數不能滿足子類的需求,此時需要在子類中對A函數進行重寫。C++中的函數重寫是指:函數名、參數、返回類型均相同。如果程序中類型兼容性原則遇到了函數重寫會怎么樣,調用父類的A函數還是子類中重寫的A函數,類型兼容與函數重寫之間的關系可以用以下程序代碼闡釋:

#include

using namespace std;

class Animal // 父類

{

public:

void Speak()

{

cout << "動物在說話" << endl;

}

};

class Dog :public Animal// 子類

{

public:

void Speak()

{

cout << "小狗在汪汪叫" << endl;

}

};

int main()

{

Dog dog;

dog.Speak();

dog.Animal::Speak();

Animal animal1 = dog;

animal1.Speak();

Animal * animal2 = & dog;

animal2->Speak();

return 0;

}

Animal animal1 = dog;

Animal * animal2 = & dog;

程序的運行結果如圖1所示。

上述程序中定義了Animal和Dog兩個類,其中,Dog類以Public方式繼承了Animal類,并且重寫了Speak()方法。根據程序運行結果不難看出:main()函數中定義的Dog類對象dog的調用方法dog.Speak()是通過子類對象的Speak()函數來實現小狗在汪汪叫功能。dog.Animal::Speak()是子類對象通過使用操作符作用域調用父類的Speak()函數來實現:動物在說話。定義的Animal的對象animal1通過調用拷貝構造函數,把dog的數據拷貝到animal1中,animal1仍為父類對象,所以animal1.Speak()執行的結果是動物在說話。最終定義了一個指向Animal類的指針animal2,將派生類對象dog的地址賦給父類指針animal2,利用該變量調用animal2–>speak()方法。得到的結果是:動物在說話。原因是C++編譯器進行了類型轉換,允許父類和子類之間進行類型轉換,即父類指針可以直接指向子類對象。根據賦值兼容,編譯器認為父類指針指向的是父類對象,因此,編譯結果只可能是調用父類中定義的同名函數。在此時,C++認為變量animal2中保存的就是Animal對象的地址,即編譯器不知道指針animal2指向的是一個什么對象,編譯器認為最安全的方法就是調用父類對象的函數,因為父類和子類肯定都有相同的Speak()函數。因此,在main()函數中執行animal2–>Speak()時,調用的是Animal對象的Speak()函數。

3.2 動態聯編與靜態聯編

以上程序出現這種情況的原因涉及C++在具體編譯過程中函數調用的問題,這種確定調用同名函數的哪個函數的過程就叫做聯編(又稱綁定)。在C++中聯編就是指函數調用與執行代碼之間關聯的過程,即確定某個標識符對應的存儲地址的過程,在C++程序中,程序的每一個函數在內存中會被分配一段存儲空間,而被分配的存儲空間的起始地址則為函數的入口地址。

按照程序聯編所進行的階段,聯編可分為兩種:靜態聯編和動態聯編。靜態聯編就是在程序的編譯與連接階段就已經確定函數調用和執行該調用的函數之間的關聯。在生成可執行文件中,函數的調用所關聯執行的代碼是確定好的,因此,靜態聯編也稱為早綁定(Early Binding)。動態聯編是在程序的運行時根據具體情況才能確定函數調用所關聯的執行代碼,因此,動態聯編也稱為晚綁定(Late Binding)[5]。

當類型兼容原則與函數重寫發生沖突時,程序員希望根據程序設計的子類對象類型來調用子類對象的函數,而不是編譯器認為的調用父類的對象函數。也就是說,如果父類指針(引用)指向(引用)父類的對象時,程序就應該調用父類的函數,如果父類指針(引用)指向(引用)子類的對象時,程序就應該調用子類的函數。這一功能可以通過動態聯編實現。與靜態聯編相比,動態聯編是在程序運行階段,根據成員函數基于對象的類型不同,編譯的結果就不同,這就是動態多態。動態多態的基礎是虛函數。虛函數是用來表現父類和子類成員函數的一種關系。

3.3 虛函數

虛函數的定義方法是用關鍵字virtual修飾類的成員函數,虛函數的定義格式:virtual〈返回值類型〉〈函數名〉(〈形式參數表〉)<函數體>。

在類的層次結構中,成員函數一旦被聲明為虛函數,那么,該類之后所有派生出來的新類中其都是虛函數。父類的虛函數在派生類中可以不重新定義,若在子類中沒有重新改寫父類的虛函數,則調用父類的虛函數。對兼容性與函數重寫程序,進行適當的修改,將父類Animal中的Speak()函數使用關鍵子Virtual將其定義為虛函數,代碼如下所示。

#include

using namespace std;

class Animal // 父類

{

public:

virtual void Speak() //用virtual 關鍵子定義Speak()為虛函數

{

cout << "動物在說話" << endl;

}

};

class Dog :public Animal// 子類Dog以public方式繼承了Animal

{

public:

void Speak()//重寫了Speak()函數

{

cout << "小狗在汪汪叫" << endl;

}

};

int main()

{

Dog dog;

dog.Speak();

dog.Animal::Speak();

Animal animal1 = dog;

animal1.Speak();

Animal * animal2 = & dog;

animal2->Speak();

return 0;

}

運行結果如圖2所示。

Animal *animal2=&dog,animal2.Speak()時,由于在父類Animal的Speak()函數前加關鍵字Virtual,使得Speak()函數變成虛函數,編譯器在編譯的時候,發現animal類中有虛函數,此時,編譯器會為每個包含虛函數的類創建一個虛函數表,該表是一個一維數組,在這個數組中存放每個虛函數的地址,這樣就實現了動態聯編,也就是晚綁定。也就實現了前面說的當調用父類指針(引用)指向(引用)子類對象函數時,調用的是子類對象的函數,實現了動態多態。

通過分析發現,要想實現動態多態要滿足以下3個條件:(1)必須存在繼承關系,程序中的Dog類以public的方式繼承了Animal類。(2)繼承關系中必須要有同名的虛函數。在兩個類中Speak()函數為同名虛函數,子類重寫父類的虛函數。(3)存在父類的指針或引用調用子類該虛函數。

了解多態是如何實現的之前,先要了解虛函數的調用原理,虛函數的調用原理和普通函數不一樣,編譯器在程序編譯的時候,發現類中有關鍵字virtual的虛函數時,編譯器會自動為每個包含虛函數的類創建一個虛函數表用來存放類對象中虛函數的地址,并同時創建一個虛函數表指針指向該虛函數表[6]。每個類使用一個虛函數表,每個類對象用一個指向虛表地址的虛表指針。父類對象包含一個指針指向父類所有虛函數的地址,子類對象也包含一個指向獨立地址的指針。如果子類沒有重新定義虛函數,該虛函數表將保存函數原始版本的地址,如果子類提供了虛函數的新定義,該虛函數表將保存新函數的地址。示例程序中定義了兩個類A和B,類B繼承自類A,父類A中定義了兩個虛函數,子類B中重寫了其中一個虛函數,代碼如下所示:

class A

{

public:

virtual void fun1()

{

cout << "fun1是類A虛函數";

}

virtual void fun2()

{

cout << "fun2是虛類A函數";

}

};

class B :public A

{

public:

virtual void fun1()

{

cout << "fun1是類B的虛函數";

}

};

分析上述程序,對于父類A中的兩個虛函數fun1()和fun2(),由于子類B重寫了類A中的fun1()函數,就導致子類B的虛函數表的第一個指針指向的是類B的fun1()的函數而不是父類A的fun1()函數,具體如表1所示。

3.4 動態多態的實現過程

編譯器進行編譯程序時發現有virtual聲明的函數,就會在這個類中產生一個虛函數表。即使子類中沒有用virtual定義虛函數,由于父類中的定義,子類通過繼承后仍為虛函數。程序中Animal類和Dog類都包含一個虛函數Speak(),因此,編譯器會為這兩個類都建立一個虛函數表,將虛函數地址存放到該表中(見圖3)。

編譯器在為每個類創建虛函數表的同時,還為每個類的對象提供了一個虛函數表指針(vfptr),虛函數表指針指向了對象所屬類的虛表。根據程序運行的對象類型去初始化虛函數表指針。虛函數表指針在沒有初始化的情況下,程序是無法調用虛函數的。虛函數表的創建和虛函數表指針的初始化是在構造函數中實現的,在構造子類對象時,先調用父類的構造函數,并初始化父類的虛函數指針,指向父類的虛函數表,當子類對象執行構造函數時,子類對象的虛函數表指針也被初始化,指向子類的虛函數表。實現了在調用虛函數時,就能夠找到正確的函數,如圖4所示。

C++編譯器在編譯時,發現Animal類的Speak()函數是虛函數,此時C++就會采用動態聯編技術。程序編譯時并不確定具體調用的函數,而是在運行時,依據對象的類型來確認調用的是哪一個函數,這種能力就叫做C++的多態性。在構造子類Dog對象dog時,按照構造函數調用的順序,先調用父類Animal的構造函數并初始化父類對象虛函數表指針,該指針指向父類的虛函數表。執行子類Dog構造函數時,子類對象的虛函數表指針被初始化,指向自身的虛函數表。Dog類的dog對象構造完畢后,其內部虛函數表指針被初始化為指向Dog類的虛表。在調用時,根據虛表中的函數地址找到Dog類的Speak()函數完成對虛函數的調用,從而實現動態綁定,實現了動態多態。

4 結語

多態性作為面向對象程序設計語言的3大要素之一,因其靈活性、伸縮性和復雜性而難以掌握。本文著重分析多態的分類、特征及動態多態的實現機制和原理,但本文對于動態多態的分析僅僅局限于單繼? 承的情況,對于多繼承的情況原理基本相同,本文未作過多說明。

參考文獻

[1]李明明,管志偉.淺析C++多態的作用及實現原理[J].無線互聯科技,2014(7):116.

[2]吳克力.C++面向對象程序設計[M].北京:清華大學出版社,2021.

[3]謝云博.多態性實現機制在C++與JAVA中的比較分析[J].軟件導刊,2014(6):45-46.

[4]姚云霞.淺析C++中類的多態性[J].隴東學院學報,2012(1):9-11.

[5]劉晨.基于靜態聯編與動態聯編多態性的研究[J].價值工程,2010(19):248-249.

[6]柯棟梁,李軍利.C++虛函數實現多態之案例驅動教學方法探討[J].安徽工業大學學報(社會科學版),2012(4):114-115.

(編輯 何 琳)

Implementation of C++ polymorphism

Li? Jiahong, Sun? Qingying*

(Huaiyin Normal University, Huaian 223300, China)

Abstract:? Polymorphism is the most important feature in C++. Skillful use of polymorphism is the key to learn C++well, while understanding the implementation mechanism and process of polymorphism is the key to use polymorphism skillfully. Based on the analysis of the basic attributes of polymorphism, this paper focuses on the implementation mechanism of dynamic polymorphism with specific program examples, and analyzes the implementation process of dynamic polymorphism with virtual function and binding principle.

Key words: C++; polymorphism; virtual function

猜你喜歡
程序
給Windows添加程序快速切換欄
電腦愛好者(2020年6期)2020-05-26 09:27:33
試論我國未決羈押程序的立法完善
人大建設(2019年12期)2019-05-21 02:55:44
失能的信仰——走向衰亡的民事訴訟程序
“程序猿”的生活什么樣
英國與歐盟正式啟動“離婚”程序程序
環球時報(2017-03-30)2017-03-30 06:44:45
基于VMM的程序行為異常檢測
偵查實驗批準程序初探
我國刑事速裁程序的構建
創衛暗訪程序有待改進
中國衛生(2015年3期)2015-11-19 02:53:32
恐怖犯罪刑事訴訟程序的完善
主站蜘蛛池模板: 无码内射在线| 一本大道香蕉中文日本不卡高清二区| 亚洲中文字幕无码mv| 99中文字幕亚洲一区二区| 日韩在线播放欧美字幕| 青青青国产视频手机| 免费毛片视频| 天天综合网在线| 欧美成人区| 免费Aⅴ片在线观看蜜芽Tⅴ | 久无码久无码av无码| 欧美国产精品不卡在线观看| 久久天天躁狠狠躁夜夜2020一| 亚洲精品第一页不卡| 国产精品自拍合集| 亚洲av日韩av制服丝袜| 美女内射视频WWW网站午夜| 国产欧美高清| 国产精品极品美女自在线看免费一区二区 | 亚洲成人播放| 国产一级小视频| 欧美福利在线观看| 亚洲欧美日本国产综合在线| 幺女国产一级毛片| 亚洲a级毛片| 婷五月综合| 亚洲黄色视频在线观看一区| 国产精品va免费视频| 久久亚洲高清国产| 亚洲女同欧美在线| 久久精品女人天堂aaa| 亚洲高清无码久久久| 久久综合一个色综合网| 一级不卡毛片| 久久77777| 99999久久久久久亚洲| 国产福利免费观看| 国产亚洲成AⅤ人片在线观看| 亚洲丝袜中文字幕| 尤物亚洲最大AV无码网站| 在线国产综合一区二区三区| 亚洲日韩国产精品综合在线观看| 国产色网站| 亚洲AV电影不卡在线观看| 亚洲三级电影在线播放| 伊人成人在线视频| 日本人妻一区二区三区不卡影院 | 国产精品香蕉在线观看不卡| 婷婷亚洲天堂| 免费又黄又爽又猛大片午夜| 国产一级在线播放| 91亚洲国产视频| 久久综合成人| 久久中文电影| 国产亚洲男人的天堂在线观看| 18黑白丝水手服自慰喷水网站| 久久国产精品麻豆系列| 免费看一级毛片波多结衣| 福利视频久久| 欧美一区二区福利视频| 亚洲国产成人在线| 欧美一级高清片欧美国产欧美| 天天综合天天综合| 中国毛片网| 九九视频免费在线观看| JIZZ亚洲国产| 国产成人做受免费视频| 中文成人在线| 在线中文字幕网| 熟妇丰满人妻| 久青草网站| 在线亚洲天堂| 亚洲无码高清一区二区| 热思思久久免费视频| 97国产在线观看| 国产亚洲成AⅤ人片在线观看| 国产高潮视频在线观看| 久久久亚洲色| 色综合日本| 99在线观看视频免费| 色网站在线免费观看| 国产在线精彩视频二区|