C++虛函數及虛函數表解析

  中心提示:虛函數必須是類的非靜態成員函數(且非構造函數), 其訪問權限是public(能夠定義爲privateorproteceted, 可是關於多態來講, 沒有意義。 在基類的類定義中定義虛函數的普通形式  虛函數的定義:  虛函數必須是類的非靜態成員函數(且非構造函數), 可是關於多態來講, 沒有意義。 ), 在基類的類定義中定義虛函數的普通形式:  virtual函數返回值類型虛函數名(形參表)  虛函數的做用是完成靜態聯編, 在定義了虛函數後, 在派生類中從新定義的函數應與虛函數具備相同的形參個數和形參類型。 以完成統一的接口, 不一樣定義過程。 若是在派生類中沒有對虛函數從新定義, 則它承繼其基類的虛函數。 當順序發現虛函數名前的關鍵字virtual後, 會自動將其做爲靜態聯編處理, 即在順序運轉時靜態地選擇合適的成員函數。   二、類之間存在子類型關係, 普通表現爲一個類從另外一個類私有派生而來。   定義虛函數的限制:  ??)非類的成員函數不能定義爲虛函數, 類的成員函數中靜態成員函數和構造函數也不能定義爲虛函數, 但能夠將析構函數定義爲虛函數。 優秀的順序員常常把基類的析構函數定義爲虛函數。 由於, 將基類的析構函數定義爲虛函數後, 系統會調用相應的類的析構函數。 而不將析構函數定義爲虛函數時,   ?而定義函數時不需求使用關鍵字「virtual」。   ?則在該類中不能出現和這個成員函數同名而且返回值、參數個數、參數類型都相同的非虛函數。 也不能出現這種非虛的同名同返回值同參數個數同參數類型函數。   爲何虛函數必須是類的成員函數:  虛函數誕生的目的就是爲了完成多態, 在類外定義虛函數毫無實際用途。   爲何類的靜態成員函數不能爲虛函數:  若是定義爲虛函數, 那麼它就是靜態綁定的,   爲何構造函數不能爲虛函數:  由於若是構造函數爲虛函數的話, 它將在執行期間被構造, 而執行期則需求對象已經樹立, 構造函數所完成的任務就是爲了樹立合適的對象, 在承繼體系中, 構造的順序就是從基類到派生類, 其目的就在於確保對象可以成功地構建。 構造函數同時承當着虛函數表的樹立, 如何確保vtbl的構建成功呢?  留意:當基類的構造函數外部有虛函數時, 又如何任務呢?與構造函數相同, 只要「局部」的版本被調用。 行爲相同, 構造函數只能調用「局部」版本, 咱們知道, 析構函數的調用順序與構造函數相反, 當某個類的析構函數被調用時, 其派生類的析構函數已經被調用了, 相應的數據也已被丟失, 若是再調用虛函數的派生類的版本, 就至關於對一些不牢靠的數據中止操做, 這是十分危險的。 所以, 虛函數機制也是不起做用的。   C++中的虛函數的做用主要是完成了多態的機制。 關於多態, 簡而言之就是用父類型別的指針指向其子類的實例, 而後通過父類的指針調用實際子類的成員函數。 這種技術可讓父類的指針有「多種外形」, 這是一種泛型技術。 虛函數技術,   關於虛函數的使用方法, 我在這裏不作過多的論述。 你們能夠看看相關的  C++的書籍。 在這篇文章中,   固然, 相同的文章在網上也出現過一些了, 沒有比擬, 沒有觸類旁通。 也但願你們多給我提意見。 讓咱們一同進入虛函數的世界。   虛函數表  對C++瞭解的人都應該知道虛函數(VirtualFunction)是通過一張虛函數表(VirtualTable)來完成的。 主是要一個類的虛函數的地址表, )中這個表被分配在了這個實例的內存中(注:一個類的虛函數表是靜態的, 他的虛函數表的是固定的, 不會爲每一個實例生成一個相應的虛函數表。 因此, 當咱們用父類的指針來操做一個子類的時分, 這張虛函數表就顯得由爲重要了, 它就像一個地圖同樣,   假定咱們有這樣的一個類:  依照下面的說法, 咱們能夠通過Base的實例來獲得Base的虛函數表。 咱們能夠通過強行把&b轉成int, 取得虛函數表的地址, 而後, 也就是Base::f(), 這在下面的順序中獲得了驗證(把int強迫轉成了函數指針)。 通過這個示例, 以下所示:  留意:在下面這個圖中, 我在虛函數表的最後多加了一個結點, 這是虛函數表的完畢結點, 就像字符串的完畢符「\0」同樣, 其標誌了虛函數表的完畢。 這個完畢標誌的值在不一樣的編譯器下是不一樣的。 我將區分說明「無覆蓋」和「有覆蓋」時的子類虛函數表的樣子。 我之因此要講述沒有覆蓋的狀況, 主要目的是爲了給一個對比。   普通承繼(無虛函數覆蓋)  下面, 再讓咱們來看看承繼時的虛函數表是什麼樣的。 假定有以下所示的一個承繼關係:  請留意, 在這個承繼關係中, 子類沒有重寫任何父類的函數。 重載就是所謂的名同而簽名不一樣, 重寫就是對子類對虛函數的從新完成。 )  咱們能夠看到下面幾點:  1)虛函數依照其聲明順序放於表中。   普通承繼(有虛函數覆蓋)  覆蓋父類的虛函數是很顯然的事情, 否則, 若是子類中有虛函數重載了父類的虛函數,   爲了讓你們看到被承繼事後的效果, 那麼, 關於派生類的實例的虛函數表會是下面的樣子:  咱們從表中能夠看到下面幾點,   2)沒有被覆蓋的函數照舊。   由b所指的內存中的虛函數表(子類的虛函數表)的f()的位置已經被Derive::f()函數地址所取代, 因而在實際調用發做時, 這就完成了多態。 再讓咱們來看看多重承繼中的狀況,   關於子類實例中的虛函數表,   2)子類的成員函數被放到了第一個父類的表中。 而可以調用到實際的函數。   多重承繼(有虛函數覆蓋)  下面咱們再來看看,   咱們能夠看見, 三個父類虛函數表中的f()的位置被交流成了子類的函數指針。 咱們就能夠用任一個父類指針來指向子類, 並調用子類的f()了。 如:  安全性  每次寫C++的文章, 這篇文章也不例外。 相信咱們對虛函數表有一個比擬細緻的瞭解了。 水可載舟, 亦可覆舟。 下面, 讓咱們來看看咱們能夠用虛函數表來乾點什麼好事吧。 子類沒有重載父類的虛函數是一件毫無心義的事情。 由於多態也是要基於函數重載的。 雖然在下面的圖中咱們能夠看到子類的虛表中有Derive本人的虛函數, 因此, 這樣的順序根本沒法編譯通過。   但在運轉時, 但這些非public的虛函數一樣會存在於子類虛函數表中,   如:  完畢語  C++這門語言是一門Magic的語言, 關於順序員來講, 咱們彷佛永遠摸不清楚這門語言揹着咱們在幹了什麼。 需求熟悉這門語言, 需求去了解C++中那些危險的東西。 否則, 這是一種搬起石頭砸本人腳的編程語言。編程

相關文章
相關標籤/搜索