關於C++函數重載及虛函數

關於C++函數重載及虛函數 
  
  

                              函數重載
如何講函數重載:
What——函數重載是什麼?
why——爲什麼要用函數重載,沒有函數重載會怎樣?
how——舉例說明怎麼使用函數重載
*******************************************************************************
    能使名字方便使用,是任何程序設計語言的一個重要特徵。
    當我們創建一個對象(即變量)時,要爲這個存儲區取一個名字。一個函數就是一個操作的名字。正是靠系統描述各種各樣的名字,我們才能寫出易於人們理解和修改的程序。象寫文章一樣,目的在於如何同讀者交流。這裏就產生了這樣一個問題:如何把人類自然語言的有細微差別的概念映射到編程語言中。
通常,自然語言中同一個詞可以代表許多種不同的含義,這要依賴上下文來確定。這就是所謂的一詞多義,從編程的角度來說就是該詞被重載了。所謂重載,從詞義上說就是重新載入,用句俗話說就是“換湯不換藥”。要學會試着理解這一點,這點非常有用,特別是對於細微的差別。具個例子:
    我們可以說“喝可樂,喝酒”。如果非得說成“喝(喝可樂的喝)可樂,喝酒(喝酒的喝)酒”,那將是很愚蠢的,就好像聽話的人對指定的動作毫無辨別能力一樣。管他和的麼事,總不是讓液體進入你的體內,至於麼樣喝,喝了以後起麼樣的反應,那就是後話了。
然而大多數編程語言要求我們爲每個函數設定一個唯一的標識符。如果我們想打印三種不同類型的數據:整型、字符型和實型,我們通常不得不用三個不同的函數名,如p r i n t _ i n t ( )、p r i n t _ c h a r ( )和p r i n t _ f l o a t ( ) ,這些既增加了我們的編程工作量,也給讀者理解程序增加了困難。舉個吃飯的例子,你桌子上面有三盤菜,你吃第一盤菜要用金筷子,第二盤,要用銀筷子,第三盤要用象牙筷子一樣,幾麻煩,用個方便筷子不是一樣的吃了。
    說白了,那雙方便筷子就是一個函數重載的實例。
    在C + +中,還有另外一個原因需要對函數名重載:構造函數。因爲構造函數的名字預先由類的名字確定,所以只能有一個構造函數名。但如果我們想用幾種方法來創建一個對象時該怎麼辦呢?例如創建一個類,它可以用標準的方法初始化,也可以從文件中讀取信息來初始化,我們就需要兩個構造函數,一個不帶參數(缺省構造函數),另一個帶一個字符串作爲參數,以表示用於初始化對象的文件的名字。(例子:兩雙方便筷子,一雙是新的剛那出來的,另一個是高溫消毒好了的!但都是方便筷子。)所以函數重載的本質就是允許函數同名。在這種情況下,構造函數是以不同的參數類型被調用的。
重載不僅對構造函數來說是必須的,對其他函數也提供了很大的方便,包括非成員函數。
另外,函數重載意味着,我們有兩個庫,它們都有一個同名的函數,只要它們的參數不同就不會發生衝突。
    我這裏講的主題不是函數重載,而是讓大家如何方便是使用“筷子”(函數名)去取用餐桌上的“美味食物”(調用函數)!
函數重載允許多個函數同名(象多種形式的筷子一樣),但還有另一種方法使函數調用更方便。如果我們想以不同的方法(象很多不同的手法那筷子一樣,左手拿,右手拿)調用同一函數,該怎麼辦呢?當函數有一個長長的參數列表,而大多數參數每次調用都一樣時,書寫這樣的函數調用會使人厭煩,程序可讀性也差。C + +中有一個很通用的作法叫缺省參數。缺省參數就是在用戶調用一個函數時沒有指定參數值而由編譯器插入參數值的參數。(象吃了飯,筷子碗一丟,不想洗碗筷,讓你父母來洗一樣。)
這樣f (“h e l l o”) , f (“h i”, 1 )和f (“h o w d y”, 2 ,‘c’)可以用來調用同一函數。它們也可能是調用三個已重載的函數,但當參數列表相同時,我們通常希望調用同一函數來完成相同的操作。
關於函數重載更深層的說明(編譯器)
void print(char);
void print(float);
    無論這兩個函數是某個類的成員函數還是全局函數都無關緊要。如果編譯器只使用函數名字的範圍,編譯器並不能產生單一的內部標識符,這兩種情況下都得用_ p r i n t結尾。(就象一雙方便筷子,光看握筷子的手,我們誰都不曉得他夾了什麼菜。)
    重載函數雖然可以讓我們有同名的函數,但這些函數的參數列表應該不一樣。所以,爲了讓重載函數正確工作,編譯器要用函數名來區分參數類型名。上面的兩個在全局範圍定義的函數,可能會產生類似於_ p r i n t _ c h a r和_ p r i n t _ f l o a t的內部名。(看到他夾了麼菜了)
    因爲,爲這樣的名字分解規定一個統一的標準毫無意義,所以不同的編譯器可能會產生不同的內部名。
    重載的函數與具有多態性的函數(即虛函數)不同處在於:調用正確的被重載函數實體是在編譯期間就被決定了的;而對於具有多態性的函數來說,是通過運行期間的動態綁定來調用我們想調用的那個函數實體。多態性是通過重定義(或重寫)這種方式達成的。請不要被重載(overloading)和重寫 (overriding)所迷惑。重載是發生在兩個或者是更多的函數具有相同的名字的情況下。區分它們的辦法是通過檢測它們的參數個數或者類型來實現的。

虛函數
什麼是虛函數???
    虛函數是指一個類中你希望重載的成員函數,當你用一個基類指針或引用指向一個繼承類對象的時候,你調用一個虛函數,實際調用的是繼承類的版本。
                                                      ——摘自MSDN
什麼是多態???
    多態是面向對象程序設計和麪向過程程序設計的主要區別之一,何謂多態?一名俗話說:“龍生九子,子子不同”多態就是同一個處理手段可以用來處理多種不同的情況。
    這裏我們主要討論虛函數的格式、條件(什麼樣的函數才叫虛函數)、調用虛函數時的注意事項。
虛函數是成員函數,而且是非static的成員函數。說明虛函數的方法如下:
virtual <類型說明符><函數名>(<參數表>)
其中,被關鍵字virtual說明的函數稱爲虛函數。
    如果某類中的一個成員函數被說明爲虛函數,這就意味着該成員函數在派生類中可能有不同的實現。(例:一句俗話就是“聾子的耳朵——擺設”,就象一個殘疾人士,可能他聽不見,但不代表他兒子聽不見,我們一般把基類叫做“父類”,派生類叫做“子類”。但我們不能說“龍生龍,鳳生風,老鼠生兒會打洞”啊!他老爸的耳朵是擺設,但在兒子這裏就是接受信息的器官了。)
      (1) 與基類的虛函數有相同的參數個數;
      (2) 其參數的類型與基類的虛函數的對應參數類型相同;
      (3) 其返回值或者與基類虛函數的相同,或者都返回指針或引用,並且派生類虛函數所返回的指針或引用的基類型是基類中被替換的虛函數所返回的指針或引用的基類型的子類型。(就像他老爹不識字,但是他可以讓他讀大學的兒子來帶他看信,讀報一樣)
    一般要求基類中說明了虛函數後,派生類說明的虛函數應該與基類中虛函數的參數個數相等,對應參數的類型相同,如果不相同,則將派生類虛函數的參數的類型強制轉換爲基類中虛函數的參數類型。
 抽象類
    帶有純虛函數的類稱爲抽象類。抽象類是一種特殊的類,它是爲了抽象和設計的目的而建立的,它處於繼承層次結構的較上層。抽象類是不能定義對象的,在實際中爲了強調一個類是抽象類,可將該類的構造函數說明爲保護的訪問控制權限。
    抽象類的主要作用是將有關的組織在一個繼承層次結構中,由它來爲它們提供一個公共的根,相關的子類是從這個根派生出來的。(例:就是一個手機模具)
    抽象類刻畫了一組子類的操作接口的通用語義,這些語義也傳給子類。一般而言,抽象類只描述這組子類共同的操作接口,而完整的實現留給子類。
    抽象類只能作爲基類來使用,其純虛函數的實現由派生類給出。如果派生類沒有重新定義純虛函數,而派生類只是繼承基類的純虛函數,則這個派生類仍然還是一個抽象類。如果派生類中給出了基類純虛函數的實現,則該派生類就不再是抽象類了,它是一個可以建立對象的具體類了。
一個實例:
class 人()
{public :
//......
void 吃()
{人吃飯;
}
//......
char *Name;
int age;
};
class 狗()
{public :
//......
void 吃()
{狗吃屎;
}
//......
char *Name;
int age;
};
    人類、狗類有一些相同的東東,如名字,年紀,吃的動作等,有人想到了爲了代碼的重用,讓人類繼承狗類,可是數據的冗餘讓這個想法完蛋了,所以有人又想出可不可以定義一個這樣的類:
    這個類界於人類狗類之間,有人類和狗類共有的一些東東,比如年紀,名字,體重什麼的,但它是不存在實例的,它的存在意義也是只是爲了派生其它類,如人類、狗類,這樣可以使系統清淅、。。。。。、、反正好處多多。
    在這個世界上根本不存在界於人狗之間的東東,所以這個“人狗之間類”中的“吃”也是沒什麼意義,你也很難爲它的內容下個定義,況且也沒必要,所以定義它爲純虛函數,形式爲:virtual void 吃()=0; 用來告訴系統:
1、這個函數可以沒有函數定義;
2、擁有本函數的類是抽象類;
你可能會說,即然純虛函數沒什麼意義,爲什麼還要它,它的作用是什麼呢?
爲實現多態作準備!!!

虛函數的作用:
之一: 虛實同型
之二: 相伴永遠
之三: 有則用之,無則虛之
關於二義性的單獨說明:
   什麼是二義性?從字面上來說就是產生的歧義,爲什麼會產生歧義?這裏我舉個例子:象你到商店去買菸,都是黃鶴樓,有150,24,18三個價位的,都是煙,也就是說煙是他們的基類,而你要買哪包抽呢?跟跟老闆將:“拿包黃鶴樓!”他哪知道你買多少錢的煙?這裏就產生了二義性!同時也引出了二義性的解決方法!老闆就會問你:“買多少錢一包的!”這裏你就可以做選擇了!但是編譯器不會開口問你調用哪個父類的成員函數!所以你要事先說明!(y::gety ())

總結:
用個例子字來總結,函數重載,是靜態的多態(靜態聯編),你可以想象成爲,你去餐館吃飯,吃着不同的菜(不同數量,不同類型的參數),用的是同一雙筷子(函數名),自己動手吃,不能指使別人幫你實現功能(自己動手,風記足食)。而虛函數,是動態的多態(動態聯編),你則可以想象爲,你要麼是兒孫滿堂的老人或者是有錢的款爺,到飯店吃大餐,自己不動手,而且吃得很講究,不同的食物要用不同的餐具(不同的對象),而且是使喚別人(子類成員函數實現功能)餵你吃。你只要指使一下(基類指針或引用指向)就OK了。在別人看來,你也是在吃飯(具有相應的功能),但是你根本沒有動手,都是要手下的小弟搞定(子類成員函數實現功能)。


 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章