1. 一般來說,如果乙個類要被另外乙個類繼承,而且用其指標指向其子類物件時,如題目中的a* d = new b();(假定a是基類,b是從a繼承而來的派生類),那麼其(a類)析構函式必須是虛的,否則在delete d時,b類的析構函式將不會被呼叫,因而會產生記憶體洩漏和異常;
2. 在構造乙個類的物件時,先構造其基類子物件,即呼叫其基類的建構函式,然後呼叫本類的建構函式;銷毀物件時,先呼叫本類的析構函式,然後再呼叫其基類的建構函式;
3. 題目給出的**是可以編譯的,但會出現執行時錯誤。錯誤出現在delete d;這一句。為討論方便,我們不妨將a類和b類改寫如下:
class a
}; class b : public a
}; 那麼a* d = new b();這一句的左邊所產生b的物件的記憶體結構如下:
而a物件的記憶體結構如下:
可見d只能找到a和a類的析構函式,而無法找到b物件的析構函式,所以當delete d;的時候,b物件所占用的記憶體就此被洩漏掉了,也從而產生了異常。
如果將a類的析構函式設為虛的,那麼a類物件的記憶體結構將為:
b類物件的記憶體結構為:
此時通過a* d = new b();,a物件的記憶體結構中的vfptr,即虛函式表指標,就是b物件的vfptr(b物件的vfptr被bitwise copy,即淺拷貝到a物件的vfptr。如b是從a虛繼承而來的,則需要加乙個offset,情況要更複雜,見
/archive/2009/04/24/4105902.aspx),因此,a物件的vfptr所指向的是b物件的虛函式表,而b的析構函式位於書函式表0的位置,因此,這樣就可以通過a類物件的指標d,找到b類物件的析構函式,從而在delete d;時,可以銷毀b物件,而不會產生記憶體洩漏和異常。
事實上,該題目只要將a中的析構函式設成虛的,b類中的析構函式前面的virtual關鍵字不管是否存在,其析構函式也一定是虛的,c類同此道理。因此,得到結論就是,只要能夠保證繼承關係中最高的基類的析構函式是虛的,那麼就不會產生前面所談及的問題。這就是為什麼在想使用多型特性的時候,需要將基類的析構函式設成虛的真正原因。
C 將析構函式定義成virtual的原因
疑問 為什麼在c 的實際使用中繼承子類的虛構函式有的時候需要新增virtual有時候不需要新增virutal 1.一般來說,如果乙個類要被另外乙個類繼承,而且用其指標指向其子類物件時,例如 a d new b 假定a是基類,b是從a繼承而來的派生類 那麼其 a類 析構函式必須是虛的,否則在delet...
C 中析構函式定義成虛函式的原因
為什麼標準c 建議將虛構函式定義成虛函式,下面就來 這個問題。include class base base private char data class baseex public base baseex private char m data void main 很顯然,上述的程式有記憶體洩漏...
C 中析構函式定義成虛函式的原因
為什麼標準c 建議將虛構函式定義成虛函式,下面就來 這個問題。include class base base private char data class baseex public base baseex private char m data void main 很顯然,上述的程式有記憶體洩漏...