c virtual 虛析構函式 資源釋放的討論

2021-09-08 23:16:47 字數 4304 閱讀 9274

關於c++ virtual,析構函式的討論已經挺多了,參見

但是我在使用時卻恰恰發生了相反的事情,不是記憶體洩漏,而是記憶體重複刪除。這也是乙個需要深思的問題。

我們還是以傳統的animal和dog為例。

首先是基類 animal

#pragma

once

class

animal;

實現

#include 

"animal.h

"#include

"stdio.h

"animal::animal(

void

):type(0)

animal::

~animal(

void

)void

animal::allocate(

intn )

void

animal::release()

puts(

"release from animal end!");

} .

派生類dog

#pragma

once

#include

"animal.h

"class

dog :

public

animal;

實現

#include 

"dog.h

"#include

"stdio.h

"dog::dog(

void

):country(0)

{}dog::

~dog(

void){}

void

dog::allocate(

intn)

void

dog::release()

puts(

"release from dog end!");

} main裡考察分別使用基類和派生類指標的情形

#include 

"dog.h

"#include

"stdio.h

"int

main()

. 執行結果是

allocate from dog start

!release from dog start

!release from animal start

!release from animal end

!release from dog end

!allocate from animal start

!allocate from animal end

!allocate from dog end

!release from animal start

!release from animal end

!a finished

!allocate from dog start

!release from dog start

!release from animal start

!release from animal end

!release from dog end

!allocate from animal start

!allocate from animal end

!allocate from dog end

!release from animal start

!release from animal end

!b finished!

兩種情形是一致的,但是要注意對於單一情形,在allocate和析構函式中,都是在基類裡呼叫了release(),但表現的結果確完全不同:

allocate成功使用了多型,析構函式卻只是呼叫了animal::release()

產生這種問題的根源在於:派生類的普通成員函式會對基類的同簽名函式產生隱藏,呼叫派生類的函式不會觸發基類的函式;但是析構函式就恰恰相反,呼叫派生類的析構函式後會緊接著呼叫基類的析構函式。

也就是說,我在dog::allocate()中顯式的呼叫animal::allocate(),也就順次顯式呼叫release(),實際上使用派生類指標呼叫基類函式,恰好滿足多型條件,因此執行正常。

但是在dog::~dog()中,我沒用顯示呼叫基類析構函式,而是系統隱式呼叫的;但是系統隱式呼叫時,派生類已經析構了,它是按照animal::~animal()來呼叫的,那麼release()也就只是有基類指標使用,因此沒有使用多型,animal發生了記憶體洩露。

那麼,我如果在dog::~dog()中顯式的呼叫release()行不行呢?

我們試試看

dog::

~dog(

void)

現在的程式輸出是

allocate from dog start

!release from dog start

!release from animal start

!release from animal end

!release from dog end

!allocate from animal start

!allocate from animal end

!allocate from dog end

!release from dog start

!release from animal start

!release from animal end

!release from dog end

!release from animal start!

沒錯,就是這些!程式就在這卡著。(我的開發環境是 vs2010+win7)

嘗試除錯,說程式陷入死鎖狀態。仔細觀察最後5句,發現 dog::~dog()裡的release()順利執行,在呼叫基類的析構函式時卡住了。類似的情況我在另乙個大點的專案中則是直接崩潰了。

分析一下這裡原因也好理解,animal::release()函式被呼叫了兩次,第二次呼叫時產生死鎖或者崩潰。

那麼是我程式**實現的有問題麼?

allocate與release都應該是virtual的吧?

dog::release()中必須要呼叫 animal::release()才合理吧?

那麼現在陷入了兩難困境:dog::~dog()中到底應不應該呼叫release()呢?呼叫,程式崩潰;不呼叫,記憶體洩露。

很讓人頭疼的問題。

無奈,我只好曲線救國,把release拆分成了兩部分來實現,總算可以實現目標,但這樣確實太醜了。

animal.h

#pragma once

class animal

;

animal.cpp

#include 

"animal.h

"#include

"stdio.h

"animal::animal(

void

):type(0)

animal::

~animal(

void

)void

animal::allocate(

intn )

void

animal::release()

void

animal::releasemydata()

puts(

"release from animal end!");

} dog.h

#pragma

once

#include

"animal.h

"class

dog :

public

animal;

dog.cpp

#include 

"dog.h

"#include

"stdio.h

"dog::dog(

void

):country(0)

{}dog::

~dog(

void

)void

dog::allocate(

intn)

void

dog::release()

void

dog::releasemydata()

puts(

"release from dog end!");

} 你聰明的,告訴我,我**錯了?有什麼解決方案啊?

C virtual 析構函式

include using std cout using std endl class base class derived public base void main 執行結果 d b 解釋 沿著程式執行流程,從 main 函式進入 首先,執行語句 base b new derived 結果是在堆...

C 虛析構函式 純虛析構函式

虛析構函式 析構函式的工作方式是 最底層的派生類 most derived class 的析構函式最先被呼叫,然後呼叫每乙個基類的析構函式。因為在c 中,當乙個派生類物件通過使用乙個基類指標刪除,而這個基類有乙個非虛的析構函式,則結果是未定義的。執行時比較有代表性的後果是物件的派生部分不會被銷毀。然...

C 虛析構函式 純虛析構函式

虛析構函式 析構函式的工作方式是 最底層的派生類 most derived class 的析構函式最先被呼叫,然後呼叫每乙個基類的析構函式。因為在c 中,當乙個派生類物件通過使用乙個基類指標刪除,而這個基類有乙個非虛的析構函式,則結果是未定義的。執行時比較有代表性的後果是物件的派生部分不會被銷毀。然...