c/c++ 多態的概念

預備知識:

c++賦值兼容原則:
    一個公有派生類的對象在使用上可以被當做基類的對象,反之則禁止。
    派生類的對象可以被賦值給基類對象。
    派生類的對象可以初始化基類的引用(指針也一樣)
  多態的概念:

  1:先期聯編:能夠在編譯時就能夠確定哪個重載的成員函數被調用的情況
  2:後期聯編(多態性):系統在運行時,能夠根據其參數類型確定調用哪個重載的成員函數的情況

多態概念介紹

所謂的多態即用父類型的指針指向子類對象,然後通過父類的指針調用實際之類的成員函數,因此父類的指針具有多種形態。多態性可以簡單地概括爲“一個接口,多種方法”,程序在運行時才決定調用的函數,它是面向對象編程領域的核心概念。

C++多態性是通過虛函數來實現的,虛函數允許子類重新定義成員函數,而子類重新定義父類的做法稱爲覆蓋(override),或者稱爲重寫。(這裏我覺得要補充,重寫的話可以有兩種,直接重寫成員函數和重寫虛函數,只有重寫了虛函數的才能算作是體現了C++多態性)而重載則是允許有多個同名的函數,而這些函數的參數列表不同,允許參數個數不同,參數類型不同,或者兩者都不同。編譯器會根據這些函數的不同列表,將同名的函數的名稱做修飾,從而生成一些不同名稱的預處理函數,來實現同名函數調用時的重載問題。但這並沒有體現多態性。

多態與非多態的實質區別就是函數地址是早綁定還是晚綁定。如果函數的調用,在編譯器編譯期間就可以確定函數的調用地址,並生產代碼,是靜態的,就是說地址是早綁定的。而如果函數調用的地址不能在編譯器期間確定,需要在運行時才確定,這就屬於晚綁定。

那麼多態的作用是什麼呢,封裝可以使得代碼模塊化,繼承可以擴展已存在的代碼,他們的目的都是爲了代碼重用。而多態的目的則是爲了接口重用。也就是說,不論傳遞過來的究竟是那個類的對象,函數都能夠通過同一個接口調用到適應各自對象的實現方法。

最常見的用法就是聲明基類的指針,利用該指針指向任意一個子類對象,調用相應的虛函數,可以根據指向的子類的不同而實現不同的方法。如果沒有使用虛函數的話,即沒有

利用C++多態性,則利用基類指針調用相應的函數的時候,將總被限制在基類函數本身,而無法調用到子類中被重寫過的函數。因爲沒有多態性,函數調用的地址將是一定的,而固定的地址將始終調用到同一個函數,這就無法實現一個接口,多種方法的目的了。


  多態性的條件:
    1:基類的虛函數。
    2:派生類的虛函數必須和基類的虛函數聲明一致(包括參數類型,返回值類)
    3:類的成員函數纔可以說明成虛函數(一般函數不行)。靜態成員函數不受制於某個對象,不能說明成虛函數。內聯函數不能在運行中動態確定。構造函數因爲負責構造對象,所以也不能是虛函數。而析構函數一般是虛函數。
     對於析構函數一般都是虛函數的解釋:
    4:指針,或者引用才能實現多態

例題:

複製代碼
 1 #include <iostream>
 2 
 3 using namespace std;
 4 
 5 class A
 6 {
 7 public:
 8     void foo()
 9     {
10     cout<<"A::function foo"<<endl;
11     }
12 virtual void fuu()
13     {
14         cout<<"A::function fuu"<<endl;
15     }
16 };
17 class B:public A
18 {
19     public:
20     void foo()
21     {
22        cout<<"B::function foo"<<endl;
23     }
24     void fuu()
25     {
26         cout<<"B::function fuu"<<endl;
27     }
28 };
29 int main()
30 {
31     A a;
32     B b;
33 
34     A *p = &a;
35     p->foo();
36     p->fuu();
37     p = &b;
38     p->foo();
39     p->fuu();
40     return 0;
41 }
複製代碼

第一個p->foo()和p->fuu()都很好理解,本身是基類指針,指向的又是基類對象,調用的都是基類本身的函數。

第二個p->foo()和p->fuu()則是基類指針指向子類對象,正式體現多態的用法,p->foo()由於指針是個基類指針,指向是一個固定偏移量的函數,因此此時指向的就只能是基類的foo()函數的代碼了。而p->fuu()指針是基類指針,指向的fuu是一個虛函數,由於每個虛函數都有一個虛函數列表,此時p調用fuu()並不是直接調用函數,而是通過虛函數列表找到相應的函數的地址,因此根據指向的對象不同,函數地址也將不同,這裏將找到對應的子類的fuu()函數的地址。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章