虛函式的目的是實現物件的動態繫結,但是有些情況下,可能換一種替代方案,可能會使類的設計更加穩定,易用,下面列出乙個我認為很棒的虛函式的替代方案。
使用non-virtual inte***ce(nvi)手法,它以public non-virtual 成員函式包裹較低訪問性的virtual函式:
class a
private:
virtual int get()
}class b:public a
}
在**裡我們可以看到虛函式不同以往的虛函式繼承措施,以往的虛函式應該是public的,然後各個派生類在各自內部以pubilc型別進行重新實現。但是上述**中將基類中的virtual函式宣告為了private型別,並用乙個non-virtual函式getval封裝了虛函式get(),該non-virtual函式只在基類中實現,派生類一律繼承。這樣可以達到和虛函式一樣的效果,一樣可以進行動態繫結。那麼一定會有同學問,這麼皮一下很開心嗎?是的,在下面說的這種情況下,很開心:
class a;
virtual int get(val value=small)
}class b:public a
}a *pa=new b();
a->get();
這個例子中,虛函式帶有預設實參,我們宣告a型別的指標指向乙個b物件,然後呼叫其中的get()函式,相信十有**眾人會立刻想,虛函式!動態繫結
!可是我在編譯器上實現過了,發現執行的函式是b類中的函式,但是get到的引數是a類中預設傳遞的引數small。這個原因是因為編譯器分為靜態繫結和動態繫結,也就說所謂的早繫結和晚繫結,對於虛函式,編譯器確實是施以了動態繫結這個高大上的設施,但是對於其中的預設實參,編譯器依然實施的是靜態繫結策略。
為什麼c++堅持以這種乖張的方式來運作呢?答案在於執行期效率,如果預設值是動態繫結,編譯器就必須有某種辦法在執行期為virtual函式決定適當的引數預設值。這比目前實行的「在編譯期決定」的機制更慢而卻更複雜。為了程式的執行速度和編譯器實現上的簡易度,c++做了這樣的取捨,其結果就是你如今所享受的執行效率。
因此,對於虛函式,本身就不應該重定義預設實參,但是既然不能重定義預設實參,每次繼承還要重新寫一遍,這就給類的定義者增加了負擔,也增加了出錯的可能。因此對於這種情況,我們就應該祭出本文所說的新**—nvi手法。
class a;
int getval(val value=small)
private:
virtual
intget(val value)
}class b:public a
}a *pa=new b();
a->getval();
這個時候,我們只需要在getval函式中設定好預設實參值,呼叫getval函式的時候會轉調虛函式get,get只接受getval函式中預設實參small,保證了派生類和基類的統一性,杜絕了出錯的可能。 一種「標準」的虛函式機制簡介
編譯器是如何針對 虛函式產生可以再執行時刻確定被呼叫函式的 呢?也就是說,虛函式實際上是如何被編譯器處理的呢?lippman在深度探索c 物件模型中的不同章節講到了幾種方式,這裡把 標準的 方式簡單介紹一下。我所說的 標準 方式,也就是所謂的 vtable 機制。編譯器發現乙個類中有被宣告為virt...
JS中一種實現sleep函式的方案
困擾了我很久的問題,在此留個記錄 原生js setinterval和settimeout 近似遞迴的處理方法 因為js是單執行緒,所以計時器也是有順序的 問題描述 我想實現乙個網頁的左右滑動效果 不是切換 解決思路 給滑動寫乙個方法,執行一次移動一小點距離,n次後移動完整張,使用setinterva...
URL重寫的一種方案
url重寫可以讓 看上去更有條理 還可以讓 改版後的舊連線能夠繼續使用。可以參考可用性專家jakob neilsen對url的建議 msdn相關參考 中文 英文 考慮到簡潔,為什麼每個url最後都要是aspx呢?因為如果不是aspx,就無法對映到aspnet中進行處理。想要實現將 2004 這樣的目...