久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 2087|回復(fù): 0
打印 上一主題 下一主題
收起左側(cè)

C++中的多態(tài)

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:108615 發(fā)表于 2016-3-13 17:04 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
1.   什么是多態(tài)多態(tài)是C++中的一個(gè)重要的基礎(chǔ),可以這樣說(shuō),不掌握多態(tài)就是C++的門(mén)個(gè)漢。我就給它定一個(gè)這樣的名字-- “調(diào)用’同名函數(shù)’卻會(huì)因上下文不同會(huì)有不同的實(shí)現(xiàn)的一種機(jī)制”。這個(gè)名字長(zhǎng)是長(zhǎng)了點(diǎn)兒,可是比“多態(tài)”清楚多了。看這個(gè)長(zhǎng)的定義,我們可以從中找出多態(tài) 的三個(gè)重要的部分。一是“相同函數(shù)名”,二是“依據(jù)上下文”,三是“實(shí)現(xiàn)卻不同”。我們且把它們叫做多態(tài)三要素吧。
2.   多態(tài)帶來(lái)的好處多態(tài)帶來(lái)兩個(gè)明顯的好處:一是不用記大量的函數(shù)名了,二是它會(huì)依據(jù)調(diào)用時(shí)的上下文來(lái)確定實(shí)現(xiàn)。確定實(shí)現(xiàn)的過(guò)程由C++本身完成,另外還有一個(gè)不明顯但卻很重要的好處是:帶來(lái)了面向?qū)ο蟮木幊獭?/font>
3.   C++中實(shí)現(xiàn)多態(tài)的方式C++中共有三種實(shí)現(xiàn)多態(tài)的方式。由“容易說(shuō)明白”到“不容易說(shuō)明白”排序分別為: 第一種是函數(shù)重載;第二種是模板函數(shù);第三種是虛函數(shù)。
4.   細(xì)說(shuō)用函數(shù)重載實(shí)現(xiàn)的多態(tài)函數(shù)重載是這樣一種機(jī)制:允許有不同參數(shù)的函數(shù)有相同的名字。具體一點(diǎn)講就是:假如有如下三個(gè)函數(shù):void test(int arg){}         //函數(shù)1void test(char arg){}         //函數(shù)2void test(int arg1,int arg2){}    //函數(shù)3如果在C中編譯,將會(huì)得到一個(gè)名字沖突的錯(cuò)誤而不能編譯通過(guò)。在C++中這樣做是合法的。可是當(dāng)我們調(diào)用test的時(shí)候到底是會(huì)調(diào)用上面三個(gè)函數(shù)中的哪一個(gè)呢?這要依據(jù)你在調(diào)用時(shí)給的出的參數(shù)來(lái)決定。如下:    test(5);       //調(diào)用函數(shù)1    test('c');//調(diào)用函數(shù)2    test(4,5); //調(diào)用函數(shù)3
C++是如何做到這一點(diǎn)的呢?原來(lái)聰明的C++編譯器在編譯的時(shí)候悄悄的在我們的函數(shù)名上根據(jù)函數(shù)的參數(shù)的不同做了一些不同的記號(hào)。具體說(shuō)如下:void test(int arg)            //被標(biāo)記為 ‘test有一個(gè)int型參數(shù)’void test(char arg)           //被標(biāo)記為 ‘test有一個(gè)char型的參數(shù)’void test(int arg1,int arg2) //被標(biāo)記為 ‘test第一個(gè)參數(shù)是int型,第二個(gè)參數(shù)為int型’這 樣一來(lái)當(dāng)我們進(jìn)行對(duì)test的調(diào)用時(shí),C++就可以根據(jù)調(diào)用時(shí)的參數(shù)來(lái)確定到底該用哪一個(gè)test函數(shù)了。噢,聰明的C++編譯器。其實(shí)C++做標(biāo)記做的 比我上面所做的更聰明。我上面哪樣的標(biāo)記太長(zhǎng)了。C++編譯器用的標(biāo)記要比我的短小的多。看看這個(gè)真正的C++的對(duì)這三個(gè)函數(shù)的標(biāo)記:?test@@YAXD@Z?test@@YAXH@Z?test@@YAXHH@Z
是不是短多了。但卻不好看明白了。好在這是給計(jì)算機(jī)看的,人看不大明白是可以理解的。還記得cout吧。我們用<<可以讓它把任意類型的數(shù)據(jù)輸出。比如可以象下面那樣:    cout << 1;    //輸出int型    cout << 8.9; //輸出double型    cout << 'a';   //輸出char型    cout << "abc";//輸出char數(shù)組型    cout << endl; //輸出一個(gè)函數(shù)cout之所以能夠用一個(gè)函數(shù)名<<(<<是一個(gè)函數(shù)名)就能做到這些全是函數(shù)重載的功能。要是沒(méi)有函數(shù)重載,我們也許會(huì)這樣使用cout,如下:    cout int<< 1;                //輸出int型    cout double<< 8.9;          //輸出double型    cout char<< 'a';            //輸出char型    cout charArray<< "abc";     //輸出char數(shù)組型    cout function(…)<< endl;   //輸出函數(shù)為每一種要輸出的類型起一個(gè)函數(shù)名,這豈不是很麻煩呀。
不過(guò)函數(shù)重載有一個(gè)美中不足之處就是不能為返回值不同的函數(shù)進(jìn)行重載。那是因?yàn)槿藗兂32粸楹瘮?shù)調(diào)用指出返回值。并不是技術(shù)上不能通過(guò)返回值來(lái)進(jìn)行重載。
5.   細(xì)說(shuō)用模板函數(shù)實(shí)現(xiàn)的多態(tài)所謂模板函數(shù)(也有人叫函數(shù)模板)是這樣一個(gè)概念:函數(shù)的內(nèi)容有了,但函數(shù)的參數(shù)類型卻是待定的(注意:參數(shù)個(gè)數(shù)不是待定的)。比如說(shuō)一個(gè)(準(zhǔn)確的說(shuō)是一類或一群)函數(shù)帶有兩個(gè)參數(shù),它的功能是返回其中的大值。這樣的函數(shù)用模板函數(shù)來(lái)實(shí)現(xiàn)是適合不過(guò)的了。如下。template < typename T>T getMax(T arg1, T arg2){    return arg1 > arg2 ? arg1:arg2; //代碼段1}這 就是基于模板的多態(tài)嗎?不是。因?yàn)楝F(xiàn)在我們不論是調(diào)用getMax(1, 2)還是調(diào)用getMax(3.0, 5.0)都是走的上面的函數(shù)定義。它沒(méi)有根據(jù)調(diào)用時(shí)的上下文不同而執(zhí)行不同的實(shí)現(xiàn)。所以這充其量也就是用了一個(gè)模板函數(shù),和多態(tài)不沾邊。怎樣才能和多態(tài)沾 上邊呢?用模板特化呀!象這樣:template<>char* getMax(char* arg1, char* arg2){    return (strcmp(arg1, arg2) > 0)?arg1:arg2;//代碼段2}這樣一來(lái)當(dāng)我們調(diào)用getMax(“abc”, “efg”)的時(shí)候,就會(huì)執(zhí)行代碼段2,而不是代碼段1。這樣就是多態(tài)了。更有意思的是如果我們?cè)賹?xiě)這樣一個(gè)函數(shù):char getMax(char arg1, char arg2){    return arg1>arg2?arg1:arg2; //代碼段3}當(dāng)我們調(diào)用getMax(‘a(chǎn)’, ‘b’)的時(shí)候,執(zhí)行的會(huì)是代碼段3,而不是代碼段1或代碼段2。C++允許對(duì)模板函數(shù)進(jìn)行函數(shù)重載,就象這個(gè)模板函數(shù)是一個(gè)普通的函數(shù)一樣。于是我們馬上能想到寫(xiě)下面這樣一個(gè)函數(shù)來(lái)做三個(gè)數(shù)中取大值的處理:int getMax( int arg1, int arg2, int arg3){    return getMax(arg1, max(arg2, arg3) ); //代碼段4}同樣我們還可以這樣寫(xiě):template <typename T>T getMax(T arg1, T arg2, T arg3){    return getMax(arg1, getMax(arg2, arg3) ); //代碼段5}現(xiàn)在看到結(jié)合了模板的多態(tài)的威力了吧。比只用函數(shù)重載厲害多了。
6.   小結(jié)上 面的兩種多態(tài)在C++中有一個(gè)總稱:靜態(tài)多態(tài)。之所以叫它們靜態(tài)多態(tài)是因?yàn)樗鼈兊亩鄳B(tài)是在編譯期間就確定了。也就是說(shuō)前面所說(shuō)的函數(shù)1,2,3代碼段1, 2,3,4,5這些,在編譯完成后,應(yīng)該在什么樣的上下文的調(diào)用中執(zhí)行哪一些就確定了。比如:如果調(diào)用getMax(0.1, 0.2, 0.3)就會(huì)執(zhí)行代碼段5。如果調(diào)用test(5)就執(zhí)行函數(shù)1。這些是在編譯期間就能確定下來(lái)的。靜態(tài)多態(tài)還有一個(gè)特點(diǎn),就是:“總和參數(shù)較勁兒”。下面所要講的一種多態(tài)就是必需是在程序的執(zhí)行過(guò)程中才能確定要真正執(zhí)行的函數(shù)。所以這種多態(tài)在C++中也被叫做動(dòng)態(tài)多態(tài)。
7.   細(xì)說(shuō)用虛函數(shù)實(shí)現(xiàn)的多態(tài)7.1.虛函數(shù)是怎么回事首先來(lái)說(shuō)一說(shuō)虛函數(shù),所謂虛函數(shù)是這樣一個(gè)概念:基類中有這么一些函數(shù),這些函數(shù)允許在派生類中其實(shí)現(xiàn)可以和基類的不一樣。在C++中用關(guān)鍵字virtual來(lái)表示一個(gè)函數(shù)是虛函數(shù)。C++中還有一個(gè)術(shù)語(yǔ) “覆蓋”與虛函數(shù)關(guān)系密切。所謂覆蓋就是說(shuō),派生類中的一個(gè)函數(shù)的聲明,與基類中某一個(gè)函數(shù)的聲明一模一樣,包括返回值,函數(shù)名,參數(shù)個(gè)數(shù),參數(shù)類型,參數(shù)次序都不能有差異。(注1說(shuō)覆蓋和虛函數(shù)關(guān)系密切的原因有兩個(gè):一個(gè)原因是,只有覆蓋基類的虛函數(shù)才是安全的。第二個(gè)原因是,要想實(shí)現(xiàn)基于虛函數(shù)的多態(tài)就必須在派生類中覆蓋基類的虛函數(shù)。接下來(lái)讓我們說(shuō)一說(shuō)為什么要有虛函數(shù),分析一下為什么派生類非要在某些情況下覆蓋基類的虛函數(shù)。就以那個(gè)非常著名的圖形繪制的例子來(lái)說(shuō)吧。假設(shè)我們?cè)跒橐粋(gè)圖形系統(tǒng)編程。我們可能有如下的一個(gè)類結(jié)構(gòu)。圖7-1形狀對(duì)外公開(kāi)一個(gè)函數(shù)來(lái)把自己繪制出來(lái)。這是合理的,形狀就應(yīng)該能繪制出來(lái),對(duì)吧?由于繼承的原因,多邊形和圓形也有了繪制自己這個(gè)函數(shù)。現(xiàn)在我們來(lái)討論在這三個(gè)類中的繪制自己的函數(shù)都應(yīng)該怎么實(shí)現(xiàn)。在形狀中嘛,什么也不做就行了。在多邊形中嘛,只要把它所有的頂點(diǎn)首尾相連起來(lái)就行了。在圓形中嘛,依據(jù)它的圓心和它的半徑畫(huà)一個(gè)360度的圓弧就行了。可是現(xiàn)在的問(wèn)題是:多邊形和圓形的繪制自己的函數(shù)是從形狀繼承而來(lái)的,并不能做連接頂點(diǎn)和畫(huà)圓弧的工作。怎 么辦呢?覆蓋它,覆蓋形狀中的繪制自己這個(gè)函數(shù)。于是我們?cè)诙噙呅魏蛨A形中各做一個(gè)繪制自己的函數(shù),覆蓋形狀中的繪制自己的函數(shù)。為了實(shí)現(xiàn)覆蓋,我們需要 把形狀中的繪制自己這個(gè)函數(shù)用virtual修飾。而且形狀中的繪制自己這個(gè)函數(shù)什么也不干,我們就把它做成一個(gè)純虛函數(shù)。純虛函數(shù)還有一個(gè)作用,就是讓 它所在的類成為抽象類。形狀理應(yīng)是一個(gè)抽象類,不是嗎?于是我們很快寫(xiě)出這三個(gè)類的代碼如下:class Shape//形狀{public:    virtualvoid DrawSelf()//繪制自己    {       cout << "我是一個(gè)什么也繪不出的圖形" << endl;    }}; class Polygo:public Shape//多邊形{public:    void DrawSelf()   //繪制自己    {       cout << "連接各頂點(diǎn)" << endl;    }}; class Circ:public Shape//圓{public:    void DrawSelf()   //繪制自己    {       cout << "以圓心和半徑為依據(jù)畫(huà)弧" << endl;    }};下面,我們將以上面的這三個(gè)類為基礎(chǔ)來(lái)說(shuō)明動(dòng)態(tài)多態(tài)。在進(jìn)行更進(jìn)一步的說(shuō)明之前,我們先來(lái)說(shuō)一個(gè)不得不說(shuō)的兩個(gè)概念:“子類型”和“向上轉(zhuǎn)型”。
7.2.向上轉(zhuǎn)型子類型很好理解,比如上面的多邊形和圓形就是形狀的子類型。關(guān)于子類型還有一個(gè)確切的定義為:如果類型X擴(kuò)充或?qū)崿F(xiàn)了類型Y,那么就說(shuō)X是Y的子類型。向 上轉(zhuǎn)型的意思是說(shuō)把一個(gè)子類型轉(zhuǎn)的對(duì)象換為父類型的對(duì)象。就好比把一個(gè)多邊形轉(zhuǎn)為一個(gè)形狀。向上轉(zhuǎn)型的意思就這么簡(jiǎn)單,但它的意義卻很深遠(yuǎn)。向上轉(zhuǎn)型中有 三點(diǎn)需要我們特別注意。第一,向上轉(zhuǎn)型是安全的。第二,向上轉(zhuǎn)型可以自動(dòng)完成。第三,向上轉(zhuǎn)型的過(guò)程中會(huì)丟失子類型信息。這三點(diǎn)在整個(gè)動(dòng)態(tài)多態(tài)中發(fā)揮著重 要的作用。假如我們有如下的一個(gè)函數(shù):void OutputShape( Shape arg)//專門(mén)負(fù)責(zé)調(diào)用形狀的繪制自己的函數(shù){    arg.DrawSelf();}那么現(xiàn)在我們可以這樣使用OutputShape這個(gè)函數(shù):    Polygon shape1;    Circ shape2;    OutputShape(shape1);    OutputShape(shape2);我們之所以可以這樣使用OutputShape函數(shù),正是由于向上轉(zhuǎn)型是安全的(不會(huì)有任何的編譯警告),是由于向上轉(zhuǎn)弄是自動(dòng)的(我們沒(méi)有自己把shape1和shape2轉(zhuǎn)為Shape類型再傳給OutputShape函數(shù))。可是上面這段程序運(yùn)行后的輸出結(jié)果是這樣的:我是一個(gè)什么也繪不出的圖形我是一個(gè)什么也繪不出的圖形明明是一個(gè)多邊形和一個(gè)圓呀,應(yīng)該是輸出這下面這個(gè)樣子才合理呀!連接各頂點(diǎn)以圓心和半徑為依據(jù)畫(huà)弧造成前面的不合理的輸出的罪魁禍?zhǔn)渍恰蛏限D(zhuǎn)型中的子類型信息丟失’。為了得到一個(gè)合理的輸出,得想個(gè)辦法來(lái)找回那些丟失的子類型信息。C++中用一種比較巧妙的辦法來(lái)找回那些丟失的子類型信息。這個(gè)辦法就是采用指針或引用。
7.3.為什么要用指針或引用來(lái)實(shí)現(xiàn)動(dòng)態(tài)多態(tài)對(duì)于一個(gè)對(duì)象來(lái)說(shuō)無(wú)論有多少個(gè)指針指向它,這些個(gè)指針?biāo)傅亩际峭粋(gè)對(duì)象。(即使你用一個(gè)void的指針指向一個(gè)對(duì)象也是這樣的,不是嗎?)同理對(duì)于引用也一樣。這究竟有多少深層次的意義呢?這里的深層的意義是這樣的:子類型的信息本來(lái)就在它本身中存在,所以我們用一個(gè)基類的指針來(lái)指出它,這個(gè)子類型的信息也會(huì)被找到,同理引用也是一樣的。C++正是利用了指針的這一特性。來(lái)做到動(dòng)態(tài)多態(tài)的。2現(xiàn)在讓我們來(lái)改寫(xiě)OutputShape函數(shù)為這樣:void OutputShape( Shape& arg)//專門(mén)負(fù)責(zé)調(diào)用形狀的繪制自己的函數(shù){    arg.DrawSelf();}現(xiàn)在我們的程序的輸出為:連接各頂點(diǎn)以圓心和半徑為依據(jù)畫(huà)弧這樣的輸出才是我們真正的想要的。我們實(shí)現(xiàn)的這種真正想要的輸出就是動(dòng)態(tài)多態(tài)的實(shí)質(zhì)。
7.4.為什么動(dòng)態(tài)多態(tài)要用public繼承在我們上面的代碼中,圓和多邊形都是從形狀公有繼承而來(lái)的。要是我們把圓的繼承改為私有或保護(hù)會(huì)怎么樣呢?我們來(lái)試一試。哇,我們得到一個(gè)編譯錯(cuò)誤。這個(gè)錯(cuò)誤的大致意思是說(shuō):“請(qǐng)不要用一個(gè)私有的方法”。怎么回事呢?是這么回事。它的意思是說(shuō)下面這樣說(shuō)不合理。所有的形狀都可以畫(huà)出來(lái),圓這種形狀是不能畫(huà)出來(lái)的。這樣合理嗎?不合理。所以請(qǐng)?jiān)诙鄳B(tài)中使用公有繼承吧。
8.   總結(jié)多態(tài)的思想其實(shí)早在面向?qū)ο蟮木幊坛霈F(xiàn)之前就有了。比如C語(yǔ)言中的+運(yùn)算符。這個(gè)運(yùn)算符可以對(duì)兩個(gè)int型的變量求和,也可以對(duì)兩個(gè)char的變量求和,也可以對(duì)一個(gè)int型一個(gè)char型的兩個(gè)變量求和。加法運(yùn)算的這種特性就是典型的多態(tài)。所以說(shuō)多態(tài)的本質(zhì)是同樣的用法在實(shí)現(xiàn)上卻是不同的。
9.   附錄:注1:嚴(yán)格地講返回值可以不同,但這種不同是有限制的。詳細(xì)情況請(qǐng)看有關(guān)協(xié)變的內(nèi)容。注2: C++會(huì)悄悄地在含有虛函數(shù)的類里面加一個(gè)指針。用這個(gè)指針來(lái)指向一個(gè)表格。這個(gè)表格會(huì)包含每一個(gè)虛函數(shù)的索引。用這個(gè)索引來(lái)找出相應(yīng)的虛函數(shù)的入口地 址。對(duì)于我們所舉的形狀的例子來(lái)說(shuō),C++會(huì)悄悄的做三個(gè)表,Shape一個(gè),Polygon一個(gè),Circ一個(gè)。它們分別記錄一個(gè)DrawSelf函數(shù) 的入口地址。在程序運(yùn)行的過(guò)程中,C++會(huì)先通過(guò)類中的那個(gè)指針來(lái)找到這個(gè)表格。再?gòu)倪@個(gè)表格中查出DrawSelf的入口地址。然后現(xiàn)通過(guò)這個(gè)入口地址 來(lái)調(diào)用正直的DrawSelf。正是由于這個(gè)查找的過(guò)程,是在運(yùn)行時(shí)完成的。所以這樣的多態(tài)才會(huì)被叫做動(dòng)態(tài)多態(tài)(運(yùn)行時(shí)多態(tài))

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表
主站蜘蛛池模板: 99这里只有精品视频 | 九九av| 亚洲色图网址 | 日韩精品一区二区三区在线播放 | 日韩欧美在线观看视频 | 久久久国产精品 | 在线视频久久 | 欧美性猛交 | 欧美日高清| 久久在线看 | 狠狠入ady亚洲精品经典电影 | 国产a区| 国产精品3区 | 亚洲成人三级 | 午夜影院在线观看 | 国产在线播放一区二区三区 | 国产精品一区二区欧美 | 亚洲国产成人精品久久久国产成人一区 | 狠狠久久 | 久久久精品影院 | 九九九视频 | 国产精品久久久久久久久久久久久 | 欧美黄色绿像 | 特级毛片www | 久久久爽爽爽美女图片 | 美女中文字幕视频 | 婷婷开心激情综合五月天 | 91精品国产一区二区三区 | 国产一区二区精品在线 | 久久婷婷国产香蕉 | 中文字幕一区二区不卡 | 五月婷婷 六月丁香 | 国产在线一区二区 | 成人免费一区二区三区视频网站 | 久久综合色综合 | 久久国产视频网站 | 国产亚洲www | 日韩午夜精品 | 国产小视频精品 | 久久久久久久久久久高潮一区二区 | 久久久久国产精品一区二区 |