當前位置:才華齋>計算機>計算機二級>

2016計算機二級考試輔導:C++多型性基本概念講述

計算機二級 閱讀(4.38K)

前幾天筆試的時候碰到考C++多型性的題目,因為不是自己的專業不是純做軟體開發,C++學習不是很好,做得有點混亂。回來以後立刻查了相關資料,大概明白了一點,可能以後解題的時候不會亂了。

2016計算機二級考試輔導:C++多型性基本概念講述

  先摘下一些網上的書上的基本概念。

多型性可以簡單地概括為“一個介面,多種方法”,程式在執行時才決定呼叫的函式,它是面向物件程式設計領域的核心概念。多型(polymorphisn),字面意思多種形狀。

C++多型性是通過虛擬函式來實現的,虛擬函式允許子類重新定義成員函式,而子類重新定義父類的做法稱為覆蓋(override),或者稱為重寫。(這裡我覺得要補充,重寫的話可以有兩種,直接重寫成員函式和重寫虛擬函式,只有重寫了虛擬函式的才能算作是體現了C++多型性)而過載則是允許有多個同名的函式,而這些函式的引數列表不同,允許引數個數不同,引數型別不同,或者兩者都不同。編譯器會根據這些函式的不同列表,將同名的函式的名稱做修飾,從而生成一些不同名稱的預處理函式,來實現同名函式呼叫時的過載問題。但這並沒有體現多型性。

多型與非多型的實質區別就是函式地址是早繫結還是晚繫結。如果函式的呼叫,在編譯器編譯期間就可以確定函式的呼叫地址,並生產程式碼,是靜態的,就是說地址是早繫結的。而如果函式呼叫的地址不能在編譯器期間確定,需要在執行時才確定,這就屬於晚繫結。

那麼多型的作用是什麼呢,封裝可以使得程式碼模組化,繼承可以擴充套件已存在的程式碼,他們的目的都是為了程式碼重用。而多型的目的則是為了介面重用。也就是說,不論傳遞過來的究竟是那個類的物件,函式都能夠通過同一個介面調用到適應各自物件的實現方法。

最常見的用法就是宣告基類的指標,利用該指標指向任意一個子類物件,呼叫相應的虛擬函式,可以根據指向的子類的不同而實現不同的方法。如果沒有使用虛擬函式的話,即沒有利用C++多型性,則利用基類指標呼叫相應的函式的時候,將總被限制在基類函式本身,而無法呼叫到子類中被重寫過的函式。因為沒有多型性,函式呼叫的地址將是一定的,而固定的地址將始終呼叫到同一個函式,這就無法實現一個介面,多種方法的目的了。

  筆試的題目

#include class A { public: void foo() { printf("1"); } virtual void fuu() { printf("2"); } }; class B:public A { public: void foo() { printf("3"); } void fuu() { printf("4"); } }; int main() { A a; B b; A *p = &a; p->foo(); p->fuu(); p = &b; p->foo(); p->fuu(); return 0; } 第一個p->foo()和p->fuu()都很好理解,本身是基類指標,指向的又是基類物件,呼叫的都是基類本身的函式,因此輸出結果就是 1、2。

第二個p->foo()和p->fuu()則是基類指標指向子類物件,正式體現多型的用法,p->foo()由於指標是個基類指標,指向是一個固定偏移量的函式,因此此時指向的就只能是基類的foo()函式的程式碼了,因此輸出的結果還是1。而p->fuu()指標是基類指標,指向的fuu是一個虛擬函式,由於每個虛擬函式都有一個虛擬函式列表,此時p呼叫fuu()並不是直接呼叫函式,而是通過虛擬函式列表找到相應的函式的地址,因此根據指向的物件不同,函式地址也將不同,這裡將找到對應的子類的()函式的地址,因此輸出的結果也會是子類的結果4.

  筆試的題目中還有一個另類測試方法。即

B *ptr = (B *)&a; ptr->foo(); ptr->fuu();

問這兩呼叫的輸出結果。這是一個用子類的指標去指向一個強制轉換為子類地址的基類物件。結果,這兩句呼叫的輸出結果是3,2。

並不是很理解這種用法,從原理上來解釋,由於B是子類指標,雖然被賦予了基類物件地址,但是ptr->foo()在呼叫的時候,由於地址偏移量固定,偏移量是子類物件的偏移量,於是即使在指向了一個基類物件的情況下,還是呼叫到了子類的函式,雖然可能從始到終都沒有子類物件的例項化出現。

而ptr->fuu()的呼叫,可能還是因為C++多型性的原因,由於指向的是一個基類物件,通過虛擬函式列表的引用,找到了基類中foo ()函式的地址,因此呼叫了基類的函式。由此可見多型性的強大,可以適應各種變化,不論指標是基類的還是子類的,都能找到正確的實現方法。