C++ 虛成員函數和動態聯編

——編譯器對非虛方法使用靜態聯編(編譯時匹配),對虛方法使用動態聯編(運行時匹配)。數組

 

  • 未使用虛方法時,指針類型在編譯時已知,所以編譯器在編譯時,能夠將成員方法關聯到相應的類,這被稱爲靜態聯編;
  • 使用虛方法時,一般只有在運行程序時才能肯定對象類型,因此編譯器生成的代碼在程序執行時將成員函數關聯到相應的類,這被稱爲動態聯編。

  靜態聯編比動態聯編效率高。函數

  虛函數的工做原理。spa

  虛函數。指針

  從新定義成員函數(改變函數特徵標)。code

  從新定義重載的成員函數。對象

 

  效率blog

  爲使程序可以在運行階段進行決策,必須採起一些方法來跟蹤基類指針或引用指向的對象類型,這增長了額外的處理開銷。所以下列狀況更適合靜態聯編:繼承

  • 類不會用做基類
  • 派生類不從新定義基類的任何方法

  所以靜態聯編被設置爲C++的默認選擇。內存

  若是要在派生類中從新定義基類的方法,則將它設置爲虛方法;不然設置爲非虛方法。原型

  

  虛函數的工做原理

  編譯器處理虛函數的方法是:給每一個對象添加一個隱藏成員。隱藏成員中保存了一個指向·函數地址·數組的指針。這種數組被稱爲虛函數表(vtbl),表中存儲了爲類對象進行聲明的虛函數的地址。

  派生類對象將包含一個指向獨立地址表的指針(即新建立一個表)。(增長內存開銷)

  調用虛函數時,程序將查看存儲在對象中的vtbl地址,而後轉向相應的函數地址表並在表中查找地址。(影響執行速度)

  總之,使用虛函數將在內存和執行速度上有必定的成本;即便非函數的效率比虛函數稍高,卻不具有動態聯編功能。

 

  構造函數不能是虛函數。

  析構函數應當是虛函數,除非類不用作基類。

  友元函數不能是虛函數,由於友元不是類成員,而只有成員才能夠是虛函數。

 

  若是派生類沒有從新定義函數,將使用該函數的基類版本(繼承它)。若是派生類位於派生鏈中,則將使用最新的虛函數版本(指針或引用調用),基類版本被隱藏的狀況除外。

   從新定義將隱藏基類方法:

class Dwelling
{
public:
    virtual void showperks(int a) const;
    ...
};
class Hovel : public Dwelling
{
public:
    virtual void showperks() const;
    ...
}

  在派生類中從新定義函數(改變了參數特徵標),將隱藏同名的基類方法,而不是重載基類方法。

Hovel trump;
trump.showperks();    // valid
trump.showperks(5);    // invalid

  若是從新定義繼承的方法,應確保與原來的原型徹底相同。若是返回類型爲基類引用或指針,則能夠修改成指向派生類的引用或指針(返回類型協變:即容許返回類型隨類類型的變化而變化)。

  若是基類聲明被重載了,則應在派生類中從新定義全部的基類版本;若是隻定義了一個版本,則其它版本將被隱藏,派生類對象將沒法使用它們。

class Dwelling
{
public:
    virtual void showperks(int a) const;
    virtual void showperks(double x) const;
    virtual void showperks() const;
    ...
};
class Hovel : public Dwelling
{
    virtual void showperks(int a) const;
    virtual void showperks(double x) const;
    virtual void showperks() const;
    ...
};

   若是不須要修改,則新定義可只調用基類版本:

  void Hovel::showperks()const {Dwelling::showperks();}  

 

-----

相關文章
相關標籤/搜索