目錄
多型的定義及實現
抽象類多型的原理
單繼承和多繼承關係的虛函式表
1、什麼是多型?
當不同的物件去完成某個行為時,會產生出不同的結果。多型是:不同繼承關係的類物件去呼叫同一函式時,產生了不同的行為。
例如:student類繼承了person類。 person物件買票全價,student物件買票半價。這就是多型行為。
2、構成多型的兩個必要條件
虛函式:在類的成員函式前面加關鍵字virtual。
虛函式的重寫:派生類中有乙個跟基類的完全相同虛函式,他們的函式名、引數、返回值都相同,我們就稱子類的虛函式重寫了基類的虛函式。另外虛函式的重寫也叫作虛函式的覆蓋。
實現乙個簡單的多型例子:
class person
};class student : public person
};void func(person& people)
void test()
3、重寫
虛函式的重寫中,派生類中重寫的成員函式可以不加virtual關鍵字,因為繼承後基類的虛函式被繼承下來了在派生類依舊保持虛函式屬性,我們只是重寫了它。但這是非常不規範的,我們平時不要這樣使用。
虛函式重寫有乙個例外:重寫的虛函式的返回值可以不同,但必須分別是基類指標和派生類指標或者基類引用和派生類引用。這種行為叫做協變。
//協變舉例
class a{};
class b : public a{};
class person
};class student : public person
};
析構函式的重寫問題:若基類的析構函式被寫成了虛函式,那麼繼承下來的派生類中是否重寫了析構函式?這裡他們看起來函式名不相同,違背了重寫的規則,但其實可以理解為編譯器對析構函式的重寫進行了特殊處理,編譯後析構函式的名稱統一處理成destructor。這也說明了基類的析構函式最好寫成虛函式。
class person
};class student : public person };
// 只有派生類student的析構函式重寫了person的析構函式,下面的delete物件呼叫析構函式,才能構成多型,才能保證p1和p2指向的物件正確的呼叫析構函式。
int main()
為什麼將析構函式寫成虛函式?如果析構函式不使用virtual,使用動態繫結,則在析構的時候就會忽略掉派生類的部分。若我們在派生類中進行了空間的開闢,而在派生類的析構中對其進行釋放,如過不呼叫派生類析構,會造成記憶體洩漏。
//基類的析構函式不是虛函式時,兩個析構函式沒有構成多型.
//在析構的時候,是根據型別析構,而不是根據物件析構,忽略了派生類的部分,會造成記憶體洩漏。
class person
};class student : public person };
int main()
4、介面繼承和實現繼承
實現繼承:普通函式的繼承是一種實現繼承,派生類繼承了基類函式,可以使用函式,繼承的是函式的實現。
介面繼承:虛函式的繼承是一種介面繼承,派生類繼承的是基類虛函式的介面,目的是為了重寫,達成多型,繼承的是介面。所以如果不實現多型,不要把函式定義成虛函式。
繼承達到的目的就是實現繼承,多型就是介面繼承。
5、過載、重寫(覆蓋)、重定義(隱藏)的對比
過載:
重寫(覆蓋):
重定義(隱藏):
純虛函式:在虛函式的後面寫上 =0 ,則這個函式為純虛函式。只對函式進行宣告,不實現。在派生類中才對於函式進行實現。
純虛函式的目的:強制重寫虛函式。
抽象類:包含純虛函式的類叫做抽象類(也叫介面類)。
class a
;class a1 : public a
};class a2 : public a
};int main()
c++11還提供了override 和 final 來修飾虛函式:
override:
虛函式的意義就是實現多型,如果沒有重寫,虛函式就沒有意義。所以c++11中使用了純虛函式 + override的方式來強制重寫虛函式。
override 修飾的派生類虛函式沒有重寫會編譯報錯。
final:
final 修飾基類的虛函式不能被派生類重寫。
1、虛函式表(虛表)
虛函式表本質是乙個存放 虛函式指標 的指標陣列,這個陣列最後麵放了乙個nullptr。虛函式指標就是類中虛函式的位址,這些虛函式的位址存放在這個指標陣列中。
類物件中有著乙個隱藏的成員指標_vfptr,我們叫做虛函式表指標,這個指標指向虛函式表。
派生類的虛表生成:
a.先將基類中的虛表內容拷貝乙份到派生類虛表中
b.如果派生類重寫了基類中某個虛函式,用派生類自己的虛函式覆蓋虛表中基類的虛函式
c.派生類自己新增加的虛函式按其在派生類中的宣告次序增加到派生類虛表的最後
虛函式存在哪?
虛函式和普通函式一樣的,都是存在**段的,只是他的指標又存到了虛表中。
虛表存在哪?
在vs下驗證後發現虛表存在**段。
虛函式表指標存在哪?
虛函式指標是存放在物件中,所以虛函式指標的位置是跟著物件的位置走的物件在棧上被建立,虛函式指標存在於棧上;物件被建立在堆上,虛函式指標就存在於堆上。
2、多型的原理
實際上的多型就是不同的物件,在呼叫時查詢其虛函式表,找到要呼叫的函式。
在派生類中,派生類的虛函式表已完成了重寫,所以儘管呼叫的是同乙個函式,但虛表卻不同,完成的是不同的動作,展現出不同的形態。
滿足多型後的函式呼叫,不是在編譯時確定的,是執行起來以後到物件的中去找的。不滿足多型的函式呼叫,是編譯時確認好的。
3、動態繫結與靜態繫結
1) 靜態繫結(前期繫結/早繫結):在程式編譯期間確定了程式的行為,也稱為靜態多型,比如:函式過載
2) 動態繫結(後期繫結/晚繫結):是在程式執行期間,根據具體拿到的型別確定程式的具體行為,呼叫具體的函式,也稱為動態多型。
1、單繼承下的虛函式表
2、多繼承下的虛函式表
物件導向三大特性之一 多型
多型 普通版本 對於同乙個方法,由於呼叫的物件不同 或者傳入的物件不同 最終實現了不同的結果 定義電腦類 class computer 在電腦類中定義乙個 sub 的規範的介面 方法 def usb self,obj obj.start 定義滑鼠類 class mouse def start sel...
php物件導向三大特性之一多型
1.抽象類是乙個特殊的方法,介面是特殊的抽象類,多型就是使用抽象類和介面 2.是物件導向設計的重要特性,它展現了動態繫結的功能,也稱為 同名異式 多型的功能可以讓軟體在開發和維護時,達到充分的延伸性。事實上,多型最直接的定義就是讓具有繼承關係的不同類物件,可以以相同名稱的成員函式呼叫,產生不同的反應...
python物件導向的三大特性之一多型
多型的特性是呼叫不同的子類將會產生不同的行為,而無需明確知道這個子類實際上是什麼 說白了就是,不同的物件呼叫相同的方法,產生不同的行為 例如 s1是字串型別,w1是列表,兩個完全不同的物件,他們都可以呼叫len方法,而得出的結果不同 多型實際上是依附於繼承的兩種含義 改變 和 擴充套件 本身就意味著...