c++程式語言是一款應用廣泛,支援多種程式設計的計算機程式語言。它的繼承、過載、多型等特性為其自身鍍上了一層層神秘的色彩,這也是為什麼c++精彩的原因,如今,眾多語言模仿c++的特性,更說明了這樣的性質的獨特之處,我們今天就會為大家詳細介紹其中c++多型性的一些基本知識,以方便大家在學習過程中對此能夠有乙個充分的掌握。
#includeusing namespace std;class a
virtual void fun()
};class b : public a
void fun()
};int main(void)
要求是寫出該函式的輸出結果。
當然,對於大牛來說,這中小菜一碟的事情肯定會成為笑柄,但從這個很小的例子,可以看出多型的很重要的性質。沒有疑問,第乙個p->foo()和p->fuu()都很好理解,本身是基類指標,指向的又是基類物件,呼叫的都是基類本身的函式,因此輸出結果就是1、2。第二個輸出結果就是1、4。p->foo()和p->fuu()則是基類指標指向子類物件,正式體現多型的用法,p->foo()由於指標是個基類指標,指向是乙個固定偏移量的函式,因此此時指向的就只能是基類的foo()函式的**了,因此輸出的結果還是1。而p->fun()指標是基類指標,指向的fun是乙個虛函式,由於每個虛函式都有乙個虛函式列表,此時p呼叫fun()並不是直接呼叫函式,而是通過虛函式列表找到相應的函式的位址,因此根據指向的物件不同,函式位址也將不同,這裡將找到對應的子類的fun()函式的位址,因此輸出的結果也會是子類的結果4。
我們來總結一下:這個題目中可以看出多型的哪些特性呢?
多型性可以簡單地概括為「乙個介面,多種方法」,程式在執行時才決定呼叫的函式,它是物件導向程式設計領域的核心概念。多型(polymorphisn),字面意思多種形狀。
c++多型性是通過虛函式來實現的,虛函式允許子類重新定義成員函式,而子類重新定義父類的做法稱為覆蓋(override),或者稱為重寫。(這裡我覺得要補充,重寫的話可以有兩種,直接重寫成員函式和重寫虛函式,只有重寫了虛函式的才能算作是體現了c++多型性)而過載則是允許有多個同名的函式,而這些函式的引數列表不同,允許引數個數不同,引數型別不同,或者兩者都不同。編譯器會根據這些函式的不同列表,將同名的函式的名稱做修飾,從而生成一些不同名稱的預處理函式,來實現同名函式呼叫時的過載問題。但這並沒有體現多型性。
多型與非多型的實質區別就是函式位址是早繫結還是晚繫結。如果函式的呼叫,在編譯器編譯期間就可以確定函式的呼叫位址,並生產**,是靜態的,就是說位址是早繫結的。而如果函式呼叫的位址不能在編譯器期間確定,需要在執行時才確定,這就屬於晚繫結。
那麼多型的作用是什麼呢,封裝可以使得**模組化,繼承可以擴充套件已存在的**,他們的目的都是為了**重用。而多型的目的則是為了介面重用。也就是說,不論傳遞過來的究竟是那個類的物件,函式都能夠通過同乙個介面呼叫到適應各自物件的實現方法。
最常見的用法就是宣告基類的指標,利用該指標指向任意乙個子類物件,呼叫相應的虛函式,可以根據指向的子類的不同而實現不同的方法。如果沒有使用虛函式的話,即沒有利用c++多型性,則利用基類指標呼叫相應的函式的時候,將總被限制在基類函式本身,而無法呼叫到子類中被重寫過的函式。因為沒有多型性,函式呼叫的位址將是一定的,而固定的位址將始終呼叫到同乙個函式,這就無法實現乙個介面,多種方法的目的了。
那麼,如果我們把上面main函式中return語句前面新增上這麼幾句:b *ptr = (b *)&a; ptr->foo(); ptr->fun();這兩次用的結果是怎麼樣的呢?
這是乙個用子類的指標去指向乙個強制轉換為子類位址的基類物件。結果,這兩句呼叫的輸出結果是3,2。
並不是很理解這種用法,從原理上來解釋,由於b是子類指標,雖然被賦予了基類物件位址,但是ptr->foo()在呼叫的時候,由於位址偏移量固定,偏移量是子類物件的偏移量,於是即使在指向了乙個基類物件的情況下,還是呼叫到了子類的函式,雖然可能從始到終都沒有子類物件的例項化出現。而ptr->fun()的呼叫,可能還是因為c++多型性的原因,由於指向的是乙個基類物件,通過虛函式列表的引用,找到了基類中fun()函式的位址,因此呼叫了基類的函式。由此可見多型性的強大,可以適應各種變化,不論指標是基類的還是子類的,都能找到正確的實現方法。
下面我們再看另乙個例子,來尋找多型的隱藏規則:
#includeusing namespace std;class base
從這個例子,我們可以知道
1、有virtual才可能發生多型現象
2、不發生多型(無virtual)呼叫就按原型別呼叫
所以,多型的隱藏規則非常令人迷惑
本來僅僅區別過載與覆蓋並不算困難,但是c++的隱藏規則使問題複雜性陡然增加。
這裡「隱藏」是指派生類的函式遮蔽了與其同名的基類函式,規則如下:
(1)如果派生類的函式與基類的函式同名,但是引數不同。此時,不論有無virtual
關鍵字,基類的函式將被隱藏(注意別與過載混淆)。
(2)如果派生類的函式與基類的函式同名,並且引數也相同,但是基類函式沒有virtual
關鍵字。此時,基類的函式被隱藏(注意別與覆蓋混淆)。
上面的程式中:
(1)函式derived::f(float)覆蓋了base::f(float)。
(2)函式derived::g(int)隱藏了base::g(float),而不是過載。
(3)函式derived::h(float)隱藏了base::h(float),而不是覆
從一道面試題談Linux下fork的執行機制
某外企面試linux開發職位面試題 給出如下c程式,在linux下使用gcc編譯 要求如下 已知從這個程式執行到這個程式的所有程序結束這個時間段內,沒有其它新程序執行。1 請說出執行這個程式後,將一共執行幾個程序。2 如果其中乙個程序的輸出結果是 pid1 1001,pid2 1002 寫出其他程序...
從一道面試題談linux下fork的執行機制
今天一位朋友去乙個不錯的外企面試linux開發職位,面試官出了乙個如下的題目 給出如下c程式,在linux下使用gcc編譯 include stdio.h include sys types.h include unistd.h int main 要求如下 已知從這個程式執行到這個程式的所有程序結束...
從一道面試題談linux下fork的執行機制
摘要 由於fork函式執行機制的複雜性,造就了當兩個fork併排時,問題就變得很複雜。解這個題的關鍵,一是要對linux下程序的機制有一定認識,二是抓住上文提到的幾個關於fork的關鍵點。今天一位朋友去乙個不錯的外企面試linux開發職位,面試官出了乙個如下的題目 給出如下c程式,在linux下使用...