-
a) 如果有繼承,構造函數會先調用父類構造函數,而如果構造函數中有虛函數,此時子類還沒有構造,所以此時的對象還是父類的,不會觸發多態。更容易記的是基類構造期間,virtual函數不是virtual函數。
-
b) 析構函數也是一樣,子類先進行析構,這時,如果有virtual函數的話,子類的內容已經被析構了,C++會視其父類,執行父類的virtual函數。
-
c) 總之,在構造和析構函數中,不要用虛函數。如果必須用,那么分離出一個Init函數和一個close函數,實現相關功能即可。
原文鏈接:https://blog.csdn.net/chen134225/article/details/81564972
第一個理由是概念上的。
在概念上,構造函數的工作是生成一個對象。在任何構造函數中,可能只是部分形成對象——我們只能知道基類已被初始化,但並不能知道哪個類是從這個基類繼承來的。然而,虛函數在繼承層次上是“向前”和“向外”進行調用。它可以調用在派生類中的函數。如果我們在構造函數中也這樣做,那么我們所調用的函數可能操作還沒有被初始化的成員,這將導致災難發生。
第二個理由是機械上的。
當一個構造函數被調用時,它做的首要的事情之一就是初始化它的VPTR。然而,它只能知道它屬於“當前”類——即構造函數所在的類。於是它完全不知道這個對象是否是基於其它類。當編譯器為這個構造函數產生代碼時,它是為這個類的構造函數產生代碼——既不是為基類,也不是為它的派生類(因為類不知道誰繼承它)。所以它使用的VPTR必須是對於這個類的VTABLE。而且,只要它是最后的構造函數調用,那么在這個對象的生命期內,VPTR將保持被初始化為指向這個VTABLE。但如果接着還有一個更晚派生類的構造函數被調用,那么這個構造函數又將設置VPTR指向它的VTABLE,以此類推,直到最后的構造函數結束。VRTP的狀態是由被最后調用的構造函數確定的。這就是為什么構造函數調用是按照從基類到最晚派生類的順序的另一個理由。
但是,當這一系列構造函數調用正發生時,每個構造函數都已經設置VPTR指向它自己的VTABLE。如果函數調用使用虛機制,它將只產生通過它自己的VTABLE的調用,而不是最后派生的VTABLE(所有構造函數被調用后才會有最后派生的VTABLE)。另外,許多編譯器認識到,如果在構造函數中進行虛函數調用,應該使用早綁定,因為它們知道晚綁定將只對本地函數產生調用。無論哪種情況,在構造函數中調用虛函數都不能得到預期的結果。
——來自《C++編程思想》合訂本第386頁
原文鏈接:https://blog.csdn.net/sumup/article/details/78174915
1. 從語法上講,調用完全沒有問題。
2. 但是從效果上看,往往不能達到需要的目的。
Effective 的解釋是:
派生類對象構造期間進入基類的構造函數時,對象類型變成了基類類型,而不是派生類類型。
同樣,進入基類析構函數時,對象也是基類類型。
所以,虛函數始終僅僅調用基類的虛函數(如果是基類調用虛函數),不能達到多態的效果,所以放在構造函數中是沒有意義的,而且往往不能達到本來想要的效果。
構造函數和析構函數調用虛函數時都不使用動態聯編,如果在構造函數或析構函數中調用虛函數,則運行的是為構造函數或析構函數自身類型定義的版本。
原因分析:
(1)不要在構造函數中調用虛函數的原因:因為父類對象會在子類之前進行構造,此時子類部分的數據成員還未初始化, 因此調用子類的虛函數是不安全的,故而C++不會進行動態聯編。
(2)不要在析構函數中調用虛函數的原因:析構函數是用來銷毀一個對象的,在銷毀一個對象時,先調用子類的析構函數,然后再調用基類的析構函數。所以在調用基類的析構函數時,派生類對象的數據成員已經“銷毀”,這個時再調用子類的虛函數已經沒有意義了。