歡迎來到c++基礎系列的部落格
我們剛才例項化物件的時候,示例一直豬是這樣寫的:
pig* pig = new pig;
但因為其是繼承animal動物類的,所以說我們其實也可以這樣寫:
animal* pig = new pig;
這種寫法一般情況下沒有毛病,但是如果子類重寫了基類的方法時,就會出現問題:呼叫的還是基類的方法。原因在於c++編譯器遵循快的原則,在animal中找到函式則直接就呼叫了,不再從子類中尋找為了避免這種情況,我們可以在基類的方法定義時,加上乙個virtual的關鍵字,這樣的話,若子類沒有重寫這個方法,那麼就會呼叫父類的方法,若子類重寫了該方法,那麼就會呼叫子類的方法。這就是虛函式,能夠讓程式的行為符合你預期的實現,同樣是c++多型性(編譯時,動態)的一部分。
純虛函式:
實現純虛函式(抽象方法)很簡單,只要在虛函式的基礎上=0就可以了。它的目的是讓子類才實現這個方法,而基類就先不定義這個函式體。
而它有以下兩個限制:1.純虛函式子類必須重寫,2.含抽象方法的類就是抽象類,而抽象類則不可以例項化。
也就是說,如果你對animal類的eat方法寫成這個樣子
virtual void eat() = 0;
那麼animal就是乙個抽象類,不能被例項化。而從實際意義上說,你可以示例話乙隻豬,乙隻狗,例項化乙個動物就是很不合理的。那麼是怎麼實現虛函式這個機制的呢?這個技術的核心是虛函式表,類的物件內部會有指向類內部的虛表位址的指標(__vptr欄位記錄)。通過這個指標呼叫虛函式。虛表是屬於類的,而不是屬於某個具體的物件,乙個類只需要乙個虛表即可。同乙個類的所有物件都使用同乙個虛表。
這種情況下,派生類中僅有乙個虛函式表。這個虛函式表和基類的虛函式表不是乙個表(無論派生類有沒有重寫基類的虛函式),但是如果派生類沒有重寫基類的虛函式的話,基類和派生類的虛函式表指向的函式位址都是相同的。
class a1
virtual void f()
virtual void g()
virtual void h()
~a1() {}
private:
int a1;
};class c : public a1
private:
int c;
};
類c沒有重寫a的虛函式,所以虛函式表內部的情況如下:
_vptr指向的位址(虛表的位址)不一樣,但是裡面的虛函式位址還是指向同乙個。
那麼如果子類重寫了積累的虛函式會怎麼樣呢?
class c : public a1
virtual void f()
virtual void g()
virtual void h()
private:
int c;
};
那麼子類虛表中所記錄的虛函式位址就會發生變化。已經和基類不是指向同乙個函式了。
如果c中寫了一些別的虛函式,那麼這些虛函式將排在父類的後面。
多繼承情況下,派生類中有多個虛函式表,虛函式的排列方式和繼承的順序一致。派生類重寫函式將會覆蓋所有虛函式表的同名內容,派生類自定義新的虛函式將會在第乙個類的虛函式表的後面進行擴充。
C 虛函式 虛表和純虛函式
定義 用virtual修飾的成員函式稱為虛函式 重寫 覆蓋 當在子類中定義了乙個與父類完全相同的虛函式時,則稱這個子類的函式重寫 或覆蓋 了父類的函式 例 includeusing namespace std class person virtual void h1 int b class deri...
C 虛函式和虛表
一直對於c 的虛函式的概念比較模糊,今天上網查資料然後把虛擬繼承這一塊給搞懂了,給大家分享。繼承是c 的一大特性,繼承是復用的重要手段,通過繼承乙個類,繼承是型別 乙個類 之間的關係建模,共享父類的一些資源,但是有些資料是共享補了的,每個類都有自己要實現的東西,所以本質是不同的。在這裡我就不再贅述繼...
C 虛函式與虛表
子類重新定義父類的函式重寫,但是只有父類函式宣告為虛函式的時候,才可以通過下面這種指標的方式進行呼叫,否則只能是呼叫父類的函式 include class base virtual void rewrite class derived public base void rewrite overrid...