c 析構函式為虛函式的問題

2021-07-05 06:14:09 字數 4350 閱讀 4410

昨天去xx公司面試,面試官問了乙個關於c++類析構函式為虛函式時,如果是父類的指標用子類來new,如果發生析構時,析構函式是virtual與不是virtual有什麼區別。當時答的不好,回來總結了一下,在機器上實現了一遍,終於搞明白了。記錄下來,以後遇到這種情況自己一定不要犯錯了

一、先看第一種最簡單的情況吧,教科書上教的,析構函式不是virtual,正常定義乙個子類物件

class student  

~student()

};

class gradeonestue:public student

~gradeonestue()

};

int _tmain(int argc, _tchar* argv)

這時構造順序是先1後2,下面是反彙編**

gradeonestue()            

00411470 push ebp

00411471 mov ebp,esp

00411473 sub esp,0cch

00411479 push ebx

0041147a push esi

0041147b push edi

0041147c push ecx

0041147d lea edi,[ebp-0cch]

00411483 mov ecx,33h

00411488 mov eax,0cccccccch

0041148d rep stos dword ptr es:[edi]

0041148f pop ecx

00411490 mov dword ptr [ebp-8],ecx

00411493 mov ecx,dword ptr [this]

00411496 call student::student (411109h)

可以看到在執行m_inum = 1前先呼叫了父類的建構函式。

再來看看析構時的順序(教科書上寫的是先呼叫子類的析構函式,在呼叫父類的,與構造過程相反)

~gradeonestue()  

0041157d mov ecx,dword ptr [this]

00411580 call student::~student (41102dh)

可以看到順序和教科書上一樣。

二、析構函式是virtual,正常定義乙個子類物件

建構函式順序就略過了,看析構彙編**

virtual ~gradeonestue()  

00411706 mov ecx,dword ptr [this]

00411709 call student::~student (411091h) ...

可以看到,析構函式最後還是呼叫了父類的析構(即使是虛函式)。

三、用乙個父類指標去new乙個子類物件,析構函式不是virtual,構造過程略過

class student  

~student()

};

class gradeonestue:public student

~gradeonestue()

};

int _tmain(int argc, _tchar* argv)

看delete pstu處的彙編**

delete pstu;  

00413726 mov eax,dword ptr [ebp-14h]

00413729 mov dword ptr [ebp-0e0h],eax

0041372f mov ecx,dword ptr [ebp-0e0h]

00413735 mov dword ptr [ebp-0ech],ecx

0041373b cmp dword ptr [ebp-0ech],0

00413742 je wmain+0c9h (413759h)

00413744 push 1

00413746 mov ecx,dword ptr [ebp-0ech]

0041374c call student::`scalar deleting destructor' (4111e5h)

00413751 mov dword ptr [ebp-10ch],eax

00413757 jmp wmain+0d3h (413763h)

00413759 mov dword ptr [ebp-10ch],0

return 0;

看到只呼叫了父類的析構函式,此時子類的析構函式沒有被呼叫,此時子類的析構函式中雖然有呼叫父類析構函式的**,但是這裡直接呼叫的是父類的析構函式,所以這是如果子類中析構函式有釋放資源的**,這裡會造成這部分資源不被釋放,有可能造成記憶體洩露

四、用乙個父類指標去new乙個子類物件,析構函式是virtual,構造過程略過

這裡直接看delete pstu的彙編**

delete pstu;  

004114e6 mov eax,dword ptr [ebp-14h]

004114e9 mov dword ptr [ebp-0e0h],eax

004114ef mov ecx,dword ptr [ebp-0e0h]

004114f5 mov dword ptr [ebp-0ech],ecx

004114fb cmp dword ptr [ebp-0ech],0

00411502 je wmain+0d9h (411529h)

00411504 mov esi,esp

00411506 push 1

00411508 mov edx,dword ptr [ebp-0ech] //edx等於pstu,指向new出來的子類物件

0041150e mov eax,dword ptr [edx] //將edx指向的dword傳給eax,eax現在是儲存的虛函式表指向的位址

00411510 mov ecx,dword ptr [ebp-0ech]

00411516 mov edx,dword ptr [eax] //將虛函式表指向的第乙個函式的位址傳給edx,也就是唯一的乙個虛析構函式的位址

00411518 call edx //呼叫析構函式,這個函式是子類的,這個位址構造時寫入虛函式表

由於子類的虛構函式最後會呼叫父類的析構函式(不管是否為virtual,子類析構函式最後都會呼叫父類析構函式),所以最終父類的析構函式會得到執行。(這時呼叫的析構函式相當於pstu->vptable->析構函式(),這個析構函式是構造時寫入的,由於構造時使用子類型別去new,此時虛函式表中析構函式的位址是子類的析構函式位址)

最後的結論:

1、無論父類與子類的析構函式是否是virtual,子類的析構函式都會呼叫父類的析構函式

2、如果父類與子類的析構函式不為virtual,用乙個父類指標指向乙個用子類型別new的物件,delete時,直接呼叫父類的析構函式,這是在編譯時刻就決定的。如果子類析構函式中有釋放資源的**,這是會發生資源洩漏。

3、如果父類與子類的析構函式是virtual,用乙個父類指標指向乙個用子類型別new的物件,delete時,這時由於是通過虛函式表呼叫析構函式,而虛函式表中的位址是構造時寫入的,是子類的析構函式的位址,由於結論第一條,所以子類與父類的析構函式都會得到呼叫,不會發生資源洩漏。

**:

C 中析構函式為虛函式

1 析構函式定義為虛函式時 基類指標可以指向派生類的物件 多型性 如果刪除該指標delete p 就會呼叫該指標指向的派生類析構函式,而派生類的析構函式又自動呼叫基類的析構函式,這樣整個派生類的物件完全被釋放。2 析構函式不定義為虛函式時 編譯器實施靜態繫結,在刪除基類指標時,只會呼叫基類的析構函式...

C 析構函式 虛析構函式

1.為什麼要定義虛析構函式?如果有乙個帶有虛函式功能的類,則它需要乙個虛析構函式,原因如下 1 如果乙個類有虛函式功能,它經常作為乙個基類使用 2 如果它是乙個基類,它的派生類經常使用new來分配 3 如果乙個派生類物件使用new來分配,並且通過乙個指向它的基類的指標來控制,那麼它經常通過乙個指向它...

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

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