天天看點

C++中的虛函數(virtual function)

一.簡介

  虛函數是C++中用于實作多态(polymorphism)的機制。核心理念就是通過基類通路派生類定義的函數。假設我們有下面的類層次:

class A

{

public:

virtual void foo() { cout << "A::foo() is called" << endl;}

};

class B: public A

virtual void foo() { cout << "B::foo() is called" << endl;}

那麼,在使用的時候,我們可以:

A * a = new B();

a->foo(); // 在這裡,a雖然是指向A的指針,但是被調用的函數(foo)卻是B的!

這個例子是虛函數的一個典型應用,通過這個例子,也許你就對虛函數有了一些概念。它虛就虛在所謂“推遲聯編”或者“動态聯編”上,一個類函數的調用并不 是在編譯時刻被确定的,而是在運作時刻被确定的。由于編寫代碼的時候并不能确定被調用的是基類的函數還是哪個派生類的函數,是以被成為“虛”函數。

虛函數隻能借助于指針或者引用來達到多态的效果,如果是下面這樣的代碼,則雖然是虛函數,但它不是多态的:

virtual void foo();

void bar()

A a;

a.foo(); // A::foo()被調用

}

1.1 多态 

在了解了虛函數的意思之後,再考慮什麼是多态就很容易了。仍然針對上面的類層次,但是使用的方法變的複雜了一些:

void bar(A * a)

a->foo(); // 被調用的是A::foo() 還是B::foo()?

因為foo()是個虛函數,是以在bar這個函數中,隻根據這段代碼,無從确定這裡被調用的是A::foo()還是B::foo(),但是可以肯定的說:如果a指向的是A類的執行個體,則A::foo()被調用,如果a指向的是B類的執行個體,則B::foo()被調用。

這種同一代碼可以産生不同效果的特點,被稱為“多态”。

1.2 多态有什麼用? 

多态這麼神奇,但是能用來做什麼呢?這個命題我難以用一兩句話概括,一般的C++教程(或者其它面向對象語言的教程)都用一個畫圖的例子來展示多态的用 途,我就不再重複這個例子了,如果你不知道這個例子,随便找本書應該都有介紹。我試圖從一個抽象的角度描述一下,回頭再結合那個畫圖的例子,也許你就更容 易了解。

在面向對象的程式設計中,首先會針對資料進行抽象(确定基類)和繼承(确定派生類),構成類層次。這個類層次的使用者在使用它們 的時候,如果仍然在需要基類的時候寫針對基類的代碼,在需要派生類的時候寫針對派生類的代碼,就等于類層次完全暴露在使用者面前。如果這個類層次有任何的 改變(增加了新類),都需要使用者“知道”(針對新類寫代碼)。這樣就增加了類層次與其使用者之間的耦合,有人把這種情況列為程式中的“bad smell”之一。

多态可以使程式員脫離這種窘境。再回頭看看1.1中的例子,bar()作為A-B這個類層次的使用者,它并不知道 這個類層次中有多少個類,每個類都叫什麼,但是一樣可以很好的工作,當有一個C類從A類派生出來後,bar()也不需要“知道”(修改)。這完全歸功于多 态--編譯器針對虛函數産生了可以在運作時刻确定被調用函數的代碼。

繼續閱讀