閱讀363 返回首頁    go 阿裏雲 go 技術社區[雲棲]


【C++】多態總結

多態

多態是如何實現綁定的?

多態綁定分兩種情況,一種是靜態綁定即編譯時多態,一種是動態綁定即運行時多態

  • 編譯時多態:

是利用重載實現的。對於非虛函數的成員來說,係統在編譯時,按照函數的參數的區別來綁定要實現的操作,在編譯時就確定了調用哪個函數。

  • 運行時多態:

簡單地說,虛函數是動態綁定的基礎;動態綁定是實現運行時多態的基礎。 要觸發動態綁定,需滿足兩個條件: (1) 隻有虛函數才能進行動態綁定,非虛函數不進行動態綁定。
(2) 必須通過基類類型的引用或指針進行函數調用。 通過基類指針或基類引用做形參,當實參傳入不同的派生類(或基類)的指針或引用,在函數內部觸發動態綁定,從而來運行時實現多態的。

所以說,為什麼調用普通函數比調用虛函數的效率高?

因為普通函數是靜態聯編的,而調用虛函數是動態聯編的。

  • 靜態聯編 :在編譯的時候就確定了函數的地址,然後call就調用了。
  • 動態聯編 : 首先需要取到對象的首地址,然後再解引用取到虛函數表的首地址後,再加上偏移量才能找到要調的虛函數,然後call調用。

明顯動態聯編要比靜態聯編做的操作多,肯定就費時間。

為什麼要用虛函數表(存函數指針的數組)?

  • 實現多態,父類對象的指針指向父類對象調用的是父類的虛函數,指向子類調用的是子類的虛函數。
  • 同一個類的多個對象的虛函數表是同一個,所以這樣就可以節省空間,一個類自己的虛函數和繼承的虛函數還有重寫父類的虛函數都會存在自己的虛函數表。

如果你想學習C/C++可以來這個裙,首先是330,中間是859,最後是766,裏麵可以學習和交流也有資料可以下載。

為什麼要把基類的析構函數定義為虛函數?

在用基類操作派生類時,為了防止執行基類的析構函數,不執行派生類的析構函數。因為這樣的刪除隻能夠刪除基類對象, 而不能刪除子類對象, 形成了刪除一半形象, 會造成內存泄漏.

為什麼要把基類的析構函數定義為虛函數就可以調用子類析構函數呢?看下麵代碼

class Base
{
public:
    Base() {};
    virtual ~Base() {};
 
    virtual void DoSomething() 
    { 
        cout << "Do something in class Base!" << endl; 
        
    };
};
 
class Derived : public Base
{
public:
    Derived() {};
    ~Derived() 
    { 
        cout << "Output from the destructor of class ClxDerived!" << endl; 
        
    };
 
    void DoSomething() 
    { 
        cout << "Do something in class ClxDerived!" << endl; 
        
    };
};
int main()
{
    //操作1
    Base* p1 = new Derived;
    p1->DoSomething();
    delete p1;
    
    //操作2
    Base* p2 = new Base;
    p2->DoSomething();
    delete p2;
}

操作1:用派生類的指針去操作派生類的成員,釋放指針P1的過程是:先釋放派生類的資源,再釋放基類資源。因為構造時先構造的基類,後構造派生類.

操作2:用基類的指針去操作派生類的成員,釋放指針P的過程是:隻是釋放了基類的資源,而沒有調用派生類的析構函數.因為派生類的DoSomething()函數重寫了基類的,所以調用的是派生類的.

  • 這樣的刪除隻能夠刪除基類對象, 而不能刪除子類對象, 形成了刪除一半形象, 造成內存泄漏.

在公有繼承中, 基類對派生類及其對象的操作,隻能影響到那些從基類繼承下來的成員.如果想要用基類對非繼承成員進行操作, 則要把基類的這個函數定義為虛函數.

所以為了解決操作2的問題,就要把基類析構函數定義成虛函數:

因為定義成虛函數後,子類的析構函數會對父類的析構函數重寫,這樣就構成多態,delete p2時,因為指向的子類對象,所以就去虛表裏調用的就是子類的析構,子類的析構函數是合成的,所以先調用子類的析構函數,再調用符類的析構函數。這樣就把子類和父類都析構了。

為什麼子類和父類的函數名不一樣,還可以構成重寫呢?

因為編譯器對析構函數的名字做了特殊處理,在內部函數名是一樣的。

抽象類

抽象類是利用虛函數的延伸

virtual void Display() = 0;//存虛函數

含有存虛函數的類為抽象類,也叫接口類,不能實例出對象

抽象類應用

抽象類就是用來繼承的,如多個子類繼承同一個抽象類,就都可以重寫這個共同父類的存虛函數,這樣的話我們用父類的指針來接收子類,我們把哪個子類傳給父類,就調用的是那個子類裏的方法,是不是很方便,抽象類也是一種規範化的編程。

C++ static

  • 類的靜態成員是屬於這個類的,屬於類的每一個對象
  • 基類的靜態成員,不管這個基類派生出多少子類,隻有這麼一個靜態成員。
  • 我們在使用時要注意:

(1)靜態方法隻能訪問類的靜態成員,不能訪問類的非靜態成員;
(2)非靜態方法可以訪問類的靜態成員,也可以訪問類的非靜態成員;
(3)靜態方法既可以用實例來調用,也可以用類名來調用。

c 中 static

  • 修飾全局變量,改變變量的連接屬性,不能其他文件調用,不改變存儲屬性。
  • 修飾局部變量,改變存儲屬性,變為存儲帶靜態全局區,不改變連接屬性,因為它屬於局部變量。

最後更新:2017-09-18 15:33:54

  上一篇:go  【C++】模板總結
  下一篇:go  如何構建大數據層級體係,看這一文章就夠了