概述:
多型是物件導向程式設計語言中資料抽象和繼承之外的第三個基本特徵(封裝,繼承,多型)。 多型性 (polymorphism)提供介面與具體實現之間的另一層隔離,從而將」what」和」how」分 離開來。多型性改善了**的可讀性和組織性,同時也使建立的程式具有可擴充套件 性,專案不僅在最初建立時期可以擴充套件,而且當專案在需要有新的功能時也能擴 展。
靜態多型和動態多型
c++支援編譯時多型(靜態多型)和執行時多型(動態多型),運算子過載和函式 過載就是編譯時多型,而派生類和虛函式實現執行時多型。 靜態多型和動態多型 的區別就是函式位址是早繫結(靜態聯編)還是晚繫結(動態聯編)。如果函式的調 用,在編譯階段就可以確定函式的呼叫位址,並產生**,就是靜態多型(編譯時多型),就是說位址是早繫結的。而如果函式的呼叫位址不能編譯不能在編譯期間 確定,而需要在執行時才能決定,這這就屬於晚繫結(動態多型,執行時多型)
多型的引入:
#include
#include
using
namespace std;
class
mycalc};
/*新增運算功能不方便*/
void
test01()
void
main()
對於上述**的話,是用來簡單計算加減乘除的,但是有個缺陷,就是如果每次想要加上別的運算,就需要在類裡面加上,如果是原始碼的話還可以改動,但是如果是庫(動態庫和靜態庫)的話就不能修改了。
多型實現簡單計算器
#include
#include
using
namespace std;
class
calc};
class
add:
public calc};
class
sub:
public calc};
intdo_calc
(int a ,
int b,calc &obj)
/*派生類可以由編譯器轉為基類*/
void
test02()
intmain()
我們可以在基類中使用乙個虛函式,通過子類來重寫這個基類中的虛函式來實現想要的功能,這個過程也叫做動態繫結,像上面的**c++動態多型性是通過虛函式來實現的,虛函式允許子類(派生類)重新定義父類 (基類)成員函式,而子類(派生類)重新定義父類(基類)虛函式的做法稱為覆 蓋(override),或者稱為重寫。 對於特定的函式進行動態繫結,c++要求在基類中宣告這個函式的時候使用 virtual 關鍵字,動態繫結也就對 virtual 函式起作用. 為創 建乙個需要動態繫結的虛成員函式,可以簡單在這個函式宣告前面加上 virtual 關 鍵字,定義時候不需要. 如果乙個函式在基類中被宣告為 virtual,那麼在所有派生 類中它都是 virtual 的. 在派生類中 virtual 函式的重定義稱為重寫(override). virtual 關鍵字只能修飾成員函式. 建構函式不能為虛函式
注意: 僅需要在基類中宣告乙個函式為 virtual.呼叫所有匹配基類宣告行為的派生類 函式都將使用虛機制。雖然可以在派生類宣告前使用關鍵字 virtual(這也是無害 的),但這個樣會使得程式顯得冗餘和雜亂。
動態繫結實現過程
當編譯器發現我們的類中有虛函式的時候,編譯器會建立一張虛函式表,把虛函式 的函式入口位址放到虛函式表中,並且在類中秘密增加乙個指標,這個指標就是 vpointer(縮寫 vptr),這個指標是指向物件的虛函式表。在多型呼叫的時候,根據 vptr 指標,找到虛函式表來實現動態繫結。
#include
#include
using
namespace std;
class
base};
classa:
public base};
void
test01()
intmain()
上述**中實現動態繫結過程的時候,在編譯期間如果基類base中存在虛函式(virtual關鍵字),就會建立虛函式表,並且把虛函式函式入口位址放入並且在類中秘密增加乙個指標,這個指標就是 vpointer(縮寫 vptr),這個指標是指向物件的虛函式表。定義派生類的物件的時候,由於派生類物件也繼承了基類的虛函式,也就是也有了虛函式指標和虛函式表,
如果子類重寫了基類中的虛函式,子類建立物件的時候就會將重寫後的虛函式入口放入所繼承的虛函式表中。在建構函式中實現此過程,不管有沒有提供建構函式,此過程都會發生。
如果子類沒有重寫基類中的虛函式,所以子類中的虛函式表中還是存的基類中的虛函式入口位址,所以會指向基類中的虛函式。
多型的成立條件:
有繼承 子類重寫父類虛函式函式 a) 返回值,函式名字,函式 引數,必須和父類完全一致(析構函式除外) b) 子類中 virtual 關鍵字可寫可不寫, 建議寫 型別相容,父類指標,父類引用 指向 子類物件
那些函式不能定義為虛函式?
1)友元函式,它不是類的成員函式
2)全域性函式
3)靜態成員函式,它沒有this指標
3)建構函式,拷貝建構函式,以及賦值運算子過載(可以但是一般不建議作為虛函式)
抽象基類和純虛函式
我們常常希望基類僅僅作為其派生類的乙個介面。這就是說,僅想對基類進 行向上型別轉換,使用它的介面,而不希望使用者實際的建立乙個基類的物件。同時 建立乙個純虛函式允許介面中放置成員原函式,而不一定要提供一段可能對這個函 數毫無意義的**。 做到這點,可以在基類中加入至少乙個純虛函式(pure virtual function),使得基類稱 為抽象類(abstract class). 純虛函式使用關鍵字 virtual,並在其後面加上=0。
如果 試圖去例項化乙個抽象類,編譯器則會阻止這種操作。 當繼承乙個抽象類的時 候,必須實現所有的純虛函式,否則由抽象類派生的類也是乙個抽象類。virtual void fun() = 0;
告訴編譯器在vtable
中為函式保留乙個位置,但在這個特定位置不放位址。 建立公共介面目的是為了將子類公共的操作抽象出來,可以通過乙個公共介面來 操縱一組類,且這個公共介面不需要實現(或者不需要完全實現)。可以建立乙個公 共類。
#include
#include
using
namespace std;
class
base};
classa:
public base
void
second()
void
third()
};classb:
public base
void
second()
void
third()
};void
test
(base* b)
/*實現簡單的業務函式用來作為公共介面*/
void
test01()
intmain()
執行結果:
虛析構函式
虛析構函式是為了解決基類的指標指向派生類物件,並用基類的指標刪除派生類物件
#include
#include
using
namespace std;
class
base};
classa:
public base};
classb:
public base};
void
test01()
intmain()
執行結果
注:就是呼叫基類的析構來析構派生類物件。
純虛構函式
純虛析構函式在 c++中是合法的,但是在使用的時候有乙個額外的限制:必須為純 虛析構函式提供乙個函式體。 那麼問題是:如果給虛析構函式提供函式體了,那 怎麼還能稱作純虛析構函式呢? 純虛析構函式和非純析構函式之間唯一的不同之 處在於純虛析構函式使得基類是抽象類,不能建立基類的物件。
#include
#include
using
namespace std;
classa;
a::~a(
)classb;
b::~b(
)void
test01()
intmain()
C 的多型特性
多型性 polymorphisn 是允許你將父物件設定成為和乙個或更多的他的子物件相等的技術,賦值之後,父物件就可以根據當前賦值給它的子物件的特性以不同的方式運作。簡單的說,就是一句話 允許將子類型別的指標賦值給父類型別的指標。多型包括靜態多型就是過載和動態多型覆蓋。這裡主要講動態多型。c 多型性是...
C 多型特性
編譯時多型性 靜態多型 通過過載函式實現 函式過載條件 形參的個數或資料型別不同 執行時多型性 動態多型 通過虛函式實現 產生多型條件 1 指向子類的基類指標2有 virtual 修飾的函式 3通過指標呼叫相應的虛函式 派生類的函式 遮蔽了與其同名的基類函式 如果派生類的函式與基類的函式同名,但是引...
C語言與C 語言中Const的特性剖析
include using namespace std int main 我們看到了,c 中的const限制了來自 從變數本身改變變數的值與從變數指標強制改變變數的值 的威脅。c 中的const型別的變數真正做到了 無法被修改 的特性,被const修改的變數也因此變為了 真正的常量 include ...