我們都知道構造函數不能為虛函數,而基類的析構函數一般都要定義為虛函數。今天重新複習了一下,特在此記載,以便查找。
構造函數不能為虛函數主要有以下兩點
1、必要性分析:
當定義派生類對象時,它會主動依次調用構造函數,順序為基類的構造函數->一級派生類構造函數->二級派生類構造函數….直到目前派生類的構造函數調用完畢為止,到此派生類對象生成。
而虛函數存在的意義為動态綁定,從上一段話可知,它會從基類開始依次自動調用相應的構造函數,根本就不存在動态綁定的必要。
2、記憶體角度分析:
構造函數的作用是生成相應的類對象。虛函數的動态綁定是依據一張虛函數表來确認的最終綁定到哪一個虛函數版本。
而調用構造函數之前,我們對類對象所做的操作僅限于配置設定記憶體,還沒有對記憶體進行初始化。此時,記憶體空間上也不存在虛函數表,是以,按照這樣的執行順序,虛函數的動态綁定是實作不了的。
基類析構函數與虛函數的關系
如果類的資料成員中不存在成員(指針)與動态配置設定的記憶體相關聯,我們一般不用自己定義析構函數,而是采用預設的析構函數析構類對象。一旦與動态配置設定的記憶體相關聯,為了防止記憶體洩露,我們需要自己定義析構函數,手動釋放動态配置設定的記憶體。
三法則:如果一個類需要析構函數,幾乎也需要定義指派構造函數和重載指派操作符。
在分析基類析構函數為什麼要定義為虛析構函數之前,我們要先明白虛函數存在的意義就是為了動态綁定,實作面向對象的特性之一 :多态。
我們知道通過基類的指針或者引用可以實作對虛函數的動态綁定,那麼當我們通過一個基類指針或者引用來析構一個對象時,我們是無法判斷基類現在綁定的對象是基類還是派生類,如果析構函數不是虛函數,那麼基類指針隻會調用基類的析構函數,如此就發生了一些不該發生的事。隻有将析構函數定義為虛函數,才能通過動态綁定,調用對應的析構函數版本,正确的析構類對象。
可以這麼說:任何class隻要有virtual函數都幾乎确定也要有一個virtual析構函數(引用自Effective C++ 條款7)