關於繼承中的析構函式

2022-05-07 02:45:12 字數 3127 閱讀 8638

#include

using namespace std;

class base

輸出:derived::f()

base::f()

為什麼呼叫了基類的析構函式,虛函式表就改變了?

這樣的問題, 有乙個最實用的方法, 就是去跟一下彙編的**, 當然了這需要你懂一點點的組合語言.

問題的原因就是: 當你呼叫基類的析構函式d.base::~base()時, c++會先把d物件中的虛函式表改變一下, 原來指向derived類的虛函式表, 現在指向base類的虛函式表. 彙編**為:

004116e3 mov eax,dword ptr [this]

004116e6 mov dword ptr [eax],offset base::`vftable'

這裡就把base的vtable給物件d了.

至於為什麼c++要這麼處理, 一樓的基本上說出來了, 就是在建構函式和析構函式裡多型性是被凍結了的, 在這兩個函式中沒有多多型性.

具體說明如下:在構造或者析構函式中呼叫沒有實現的純虛函式是很恐怖的,但是一般這個動作編譯器都能檢查出來;但是如果中間隔了一層(構造函式呼叫非純虛函式a,而a呼叫了純虛函式),編譯器就無能為力了,然後就等著執行時錯誤出現吧~~~

而且,在建構函式或析構函式的上下文中,虛函式是沒有意義的,它只呼叫本類的那個函式。因為,就建構函式來說,基類的建構函式總是先呼叫,而根據前面所說的,基類的建構函式中會被編譯器插入一些填寫本型別虛函式表的**,也就是在執行基類建構函式中的使用者**時,此時虛函式表中的儲存的僅是本型別的虛函式位址。也就是在構造時,多型是被freeze的。對應析構函式則是相反的過程,原理是一樣的

至於析構函式還執行其它什麼任務了, 我帖出來這段的彙編**你就知道了:

~base()

00411db4 pop edi

00411db5 pop esi

00411db6 pop ebx

00411db7 add esp,0cch

00411dbd cmp ebp,esp

00411dbf call @ilt+460(__rtc_checkesp) (4111d1h)

00411dc4 mov esp,ebp

00411dc6 pop ebp

00411dc7 ret

說實話, 除了你碰到的這個問題, 還真是沒有其它什麼值得你注意的問題了.

測試**1:

#include "stdafx.h"

using namespace std;

class classa

{public:

classa(){

cout<<"classa::classa() begin"cout<<"------------"<

在vs2003執行結果如下

classa::classa() begin

classa::print()

classa::classa() end

classb::classb() begin

classb::print()

classb::classb() end

------------

classb::print()

------------

classb::~classb() begin

classb::print()

classb::~classb() end

classa::~classa() begin

classa::print()

classa::~classa() end

(對**稍作修改,在gcc中的編譯結果也是如此)

可以看到,在new classb時,雖然在父類classa的建構函式調了的是被classb覆蓋的虛函式print(),但是實際上還是呼叫的classa的print。這是因為

繼承類在構造的時候總是首先呼叫其基類的建構函式來對屬於其基類的部分進行構造,在這個時候,整個類被當作基類來處理(此時虛指標指向基類的虛函式表),繼承類的部分對整個類來說好像不存在一樣,直到基類的建構函式退出並進入繼承類的建構函式,該類才被當作繼承類來出來處理(此時虛指標指向派生類的虛函式表)。對析構也一樣,只是析構的順序正好相反。

進一步分析,如果在析構函式中呼叫純虛函式呢?將classa中的print()改為純虛函式

測試**2:

#include "stdafx.h"

using namespace std;

class classa

{public:

classa(){

cout<<"classa::classa() begin"cout<<"------------"<

編譯出錯:

之所以出錯注意看上面的紅字,此時在基類classa中構造析構函式呼叫的print()是本類的,未實現。故報錯。因為已經強調,在析構函式建構函式中就是當作本類(範圍,虛指標都與本類搭配)。如果不是構造析構函式,若是其他函式,那麼就可以呼叫純虛函式。因為有純虛函式的類是抽象類並不會建立例項。

test error lnk2019: 無法解析的外部符號 "public: virtual void __thiscall classa::print(void)" (?print@classa@@uaexxz) ,該符號在函式 "public: __thiscall classa::classa(void)" (??0classa@@qae@xz) 中被引用

gcc中也有類似提示

稍加改動,繼續測試:

測試**3:

#include "stdafx.h"

using namespace std;

class classa

{public:

classa(){

cout<<"classa::classa() begin"cout<<"------------"<

成功編譯,但是執行時出現runtime error的錯誤。編譯器不會繞彎啊。

最後,建議大家在建構函式中別做太複雜的事,最好只是對成員變數的初

始化工作。複雜點的操作另寫乙個初始化函式。

關於繼承中的析構函式

class clxbase clxbase virtual void dosomething class clxderived public clxbase clxderived void dosomething int main clxderived p new clxderived 情況 clx...

關於析構函式

q1 析構函式是幹什麼的?a1 析構函式用來釋放物件所分配的資源。舉例來說,lock 類可能鎖定了乙個訊號量,那麼析構函式將釋放該訊號量。最常見的例子是,當建構函式中使用了new,那麼析構函式則使用delete。q2 物件的析構順序?a2 與建構函式相反,先構造的後析構。如 乙個物件陣列構造順序是0...

繼承中建構函式與析構函式

include using namespace std class y y int ii i ii class x public y 這個是繼承,在繼承的時候,x 類中含有的函式與y 函式相同,則將y類的此函式遮蔽掉,無論引數是否相同,只要函式一樣就將其遮蔽掉,class base1 base1 c...