虛函式及靜態聯編與動態聯編 學習筆記

2021-06-23 08:51:22 字數 2491 閱讀 1877

c++ primer plus 第13章 類繼承

由於虛函式與靜動態聯編關係緊密,所以放在一處詳細說明,令請見另一篇文章virtual屬性對類繼承的影響---學習筆記,此文章包含了下面1中第二點的詳細描述及乙個示例。

1、提到靜態聯編與動態聯編,我們首先需要來講講virtual函式,虛函式的一些要點:

在基類方法的宣告中使用關鍵字virtual可使該方法在基類以及所有的派生類(包括從派生類派生出來的類)中是虛擬的。

如果使用指向物件的引用或指標來呼叫虛方法,程式將使用為物件型別定義的方法,而不使用為引用或指標型別定義的方法。(詳見virtual屬性對類繼承的影響---學習筆記)這稱為動態聯編或晚期聯編

。這種行為非常重要,因為這樣積累指標或引用可以指向派生類物件。

如果定義的類將被用作基類,則應將那些要在派生類中重新定義的類方法宣告為虛擬的。

2、虛方法的一些其它知識

a、建構函式

建構函式不能是虛函式。建立派生類物件時,將呼叫派生類的建構函式,而不是基類的建構函式,然後,派生類的建構函式將使用基類的乙個建構函式,這種順序不同於繼承機制。因此派生類不繼承基類的建構函式,所以將基類建構函式宣告為虛擬的沒有什麼意義。

b、析構函式

析構函式應當是虛函式,除非類不用做基類。例如,假設employee是基類,singer是派生類,並新增乙個 char* 成員,該成員指向由 new 分配的記憶體。當 singer 物件過期時,必須呼叫 ~singer() 析構函式來釋放記憶體。

// 請看下面的**:

employee *pe = new singer; // legal because employee is base for singer

// ...

delete pe; // ~employee() or ~singer();

如果使用預設的靜態聯編(即不使用 virtual ),delete 語句將呼叫 ~employee() 析構函式。這將釋放由 singer 物件中的 employee 部分指向的記憶體,但不會釋放新的類成員指向的記憶體。但如果析構函式是虛擬的,則上述**將先呼叫 ~singer() 析構函式釋放由 singer 元件指向的記憶體,然後呼叫 ~employee() 析構函式來釋放由 employee 元件指向的記憶體。(此處解釋參考1中的第二點,同樣也可以參考

virtual屬性對類繼承的影響---學習筆記)

這意味著,即使基類不需要顯示析構函式提供服務,也不應依賴於預設建構函式,而應提供虛擬析構函式,即使它不執行任何操作: virtual ~baseclass()

順便說一句,給類定義乙個虛擬析構函式並非錯誤,即使這個類不用做基類,這只是乙個效率方面的問題。

c、友元

友元不能是虛函式,因為友元不是類成員,而只有成員才能是虛函式。如果由於這個原因引起了設計問題,可以通過讓友元函式使用虛擬成員函式來解決。

d、沒有重新定義(即在繼承類中沒有實現該虛函式)

如果派生類沒有重新定義函式,將使用該函式的基類版本。如果派生類位於派生鏈中,則將使用最新的虛函式版本,例外的情況是基類版本是隱藏的。

e、重新定義隱藏方法(函式隱藏)

// 假設建立了如下所示的**:

class dwelling

;class hovel : public dwelling

;

這個可能不會出現警告(取決與編譯器),也可能會出現類似於這樣的編譯器警告:warning:  hovel::showperks(void) hides dewlling::showperks(int)

**將具有如下含義:

hovel trump;

trump.showperks();    // valid

trump.showperks(5); // invalid

新定義將 showperks() 定義為乙個不接受任何引數的函式。重新定義不會生成函式的兩個過載版本,而是隱藏了接收乙個 int 引數的基類版本。簡而言之,重新定義繼承的方法並不是過載。如果在派生類中重新定義函式,將不是使用相同的函式特徵覆蓋基類宣告,而是隱藏同名的基類方法,不管引數特徵標如何。

這引出了兩條經驗規則:第一,如果重新定義繼承的方法,應確保與原理啊的原型完全相同,但如果返回型別是基類引用或指標,則可以修改為指向派生類的引用或指標(這種例外是新出現的)。這種特徵被稱為返回型別協變(covariance of return type),因為允許返回型別隨類的型別變化而變化。

class dewlling

;class hovel : public dwelling

;

注意,這種例外只適用於返回值,而不適用於引數。

第二,如果基類宣告被過載了,則應在派生類中重新定義所有的基類版本。

class dwelling

;class hovel : public dwelling

;

如果只重新定義乙個版本,則另外兩個版本將被隱藏,派生類物件將無法使用它們。注意,如果不需要修改,則新定義可只呼叫基類版本。

靜態聯編與動態聯編

在c 中,多型性主要是通過函式過載實現的。過載函式是指程式中對同名函式進行呼叫時,編譯器會根據函式引數的型別和個數,決定該呼叫哪一段函式 來處理這個函式呼叫。這種把函式呼叫與適當的函式 相對應的動作,叫做聯編。聯編分為靜態聯編和動態聯編。在編譯階段決定執行哪個同名的被呼叫函式,稱為靜態聯編。在編譯階...

動態聯編與靜態聯編

首先,聯編是指乙個電腦程式的不同部分彼此關聯的過程。靜態聯編是指聯編工作在編譯階段完成的,這種聯編過程是在程式執行之前完成的,又稱為早期聯編。要實現靜態聯編,在編譯階段就必須確定程式中的操作呼叫 如函式呼叫 與執行該操作 間的關係,確定這種關係稱為束定,在編譯時的束定稱為靜態束定。靜態聯編對函式的選...

動態聯編與靜態聯編

include using namespace std class base 輸出為 f1 of base destructor base f1 of derive destructor base 說明 只要將基類的函式設定為虛函式,那麼所有覆蓋它的子類的函式也都是虛函式,而不需要再使用virtua...