1. 程式人生 > >C++三大特性之多態

C++三大特性之多態

類函數 如果 ide 賦值 純虛函數 cti turn 重新 git

原文地址:https://qunxinghu.github.io/2016/09/08/C++%20%E4%B8%89%E5%A4%A7%E7%89%B9%E6%80%A7%E4%B9%8B%E5%A4%9A%E6%80%81/

概念

多態(Polymorphisn)

多態性是允許你將父對象設置成為和一個或更多的他的子對象相等的技術,賦值之後,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作。簡單的說:允許將子類類型的指針賦值給父類類型的指針(一個接口,多種方法)。
C++ 支持兩種多態性:編譯時多態性,運行時多態性。
a、編譯時多態性(靜態多態):通過重載函數實現
b、運行時多態性(動態多態):通過虛函數實現。

多態的作用

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

多態的用法

運行時多態:

最常見的用法就是聲明基類的指針,利用該指針指向任意一個子類對象,調用相應的虛函數,可以根據指向的子類的不同而實現不同的方法。如果沒有使用虛函數的話,則利用基類指針調用相應的函數的時候,將總被限制在基類函數本身,而無法調用到子類中被重寫過的函數。因為沒有多態性,函數調用的地址將是一定的,而固定的地址將始終調用到同一個函數,這就無法實現一個接口,多種方法的目的了。

非運行時多態:

通過函數重載實現

簡而言之:

  1. 有virtual才可能發生動態多態現象
  2. (無virtual)調用就按原類型調用

令人迷惑的隱藏規則

隱藏是指派生類的函數屏蔽了與其同名的基類函數,規則如下:

  • 如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual關鍵字,基類的函數將被隱藏(註意別與重載混淆,重載是在同一個類中,而隱藏涉及派生類與基類)。
  • 如果派生類的函數與基類的函數同名,並且參數也相同,但是基類函數沒有virtual關鍵字。此時,基類的函數被隱藏(註意別與覆蓋混淆,覆蓋有virtual關鍵字)。

C++虛函數

虛函數: 就是允許被其子類重新定義的成員函數,子類重新定義父類虛函數的做法,可實現成員函數的動態覆蓋(Override)。

純虛函數: 是在基類中聲明的虛函數,它在基類中沒有定義,但要求任何派生類都要定義自己的實現方法。在基類中實現純虛函數的方法是在函數原型後加“=0”

virtual void funtion()=0

抽象類: 包含純虛函數的類稱為抽象類。由於抽象類包含了沒有定義的純虛函數,所以不能進行實例化。

純虛函數的作用:

  1. 為了方便使用多態特性,我們常常需要在基類中定義虛擬函數。
  2. 在很多情況下,基類本身生成對象是不合情理的。例如,動物作為一個基類可以派生出老虎、孔雀等子類,但動物本身生成對象明顯不合常理。
    為了解決上述問題,引入了純虛函數的概念,將函數定義為純虛函數(方法:virtual ReturnType Function()= 0;),則編譯器要求在派生類中必須予以重寫以實現多態性。同時含有純虛擬函數的類稱為抽象類,它不能生成對象。這樣就很好地解決了上述兩個問題。

示例代碼:

#include<iostream>
using namespace std;

//基類對象
class Base
{
public:
    //有virtual關鍵字,運行時多態
    virtual void f(float x)
    {
        cout<<"Base::f(float)"<< x <<endl;
    }
    //無viratul關鍵字,不會發生運行時多態
    void g(float x)
    {
        cout<<"Base::g(float)"<< x <<endl;
    }
    void h(float x)
    {
        cout<<"Base::h(float)"<< x <<endl;
    }
};
class Derived : public Base
{
public:
    virtual void f(float x)
    {
        cout<<"Derived::f(float)"<< x <<endl;   //多態、覆蓋
    }
    //子類與父類的函數同名,無virtual關鍵字,則為隱藏
    void g(int x)
    {
        cout<<"Derived::g(int)"<< x <<endl;     //隱藏
    }
    void h(float x)
    {
        cout<<"Derived::h(float)"<< x <<endl;   //隱藏
    }
};
int main(void)
{
    Derived d;  //子類
    Base *pb = &d;  //基類指針指向子類
    Derived *pd = &d;   //子類指針指向自己
    // Good : behavior depends solely on type of the object
    pb->f(3.14f);   // Derived::f(float) 3.14   調用子類,多態
    pd->f(3.14f);   // Derived::f(float) 3.14   調用子類

    // Bad : behavior depends on type of the pointer
    pb->g(3.14f);   // Base::g(float)  3.14 無多態,調用自己的
    pd->g(3.14f);   // Derived::g(int) 3    無多態,調用自己的

    // Bad : behavior depends on type of the pointer
    pb->h(3.14f);   // Base::h(float) 3.14  無多態,調用自己的
    pd->h(3.14f);   // Derived::h(float) 3.14   無多態,調用自己的
    return 0;
}

C++三大特性之多態