C++中基類的析構函數為什么要用virtual虛析構函數

from:https://blog.csdn.net/iicy266/article/details/11906457

知識背景

? ? ? ? ?要弄明白這個問題,首先要了解下C++中的動態綁定。?

? ? ? ? ?關于動態綁定的講解,請參閱: ?C++中的動態類型與動態綁定、虛函數、多態實現

正題

? ? ? ? ?直接的講,C++中基類采用virtual虛析構函數是為了防止內存泄漏。具體地說,如果派生類中申請了內存空間,并在其析構函數中對這些內存空間進行釋放。假設基類中采用的是非虛析構函數,當刪除基類指針指向的派生類對象時就不會觸發動態綁定,因而只會調用基類的析構函數,而不會調用派生類的析構函數。那么在這種情況下,派生類中申請的空間就得不到釋放從而產生內存泄漏。所以,為了防止這種情況的發生,C++中基類的析構函數應采用virtual虛析構函數。

示例代碼講解

現有Base基類,其析構函數為非虛析構函數。Derived1和Derived2為Base的派生類,這兩個派生類中均有以string* 指向存儲其name的地址空間,name對象是通過new創建在堆上的對象,因此在析構時,需要顯式調用delete刪除指針歸還內存,否則就會造成內存泄漏。

[cpp]?view plaincopy
  1. class?Base?
  2. {??
  3. ? ?public:??
  4. ? ? ~Base()?{??
  5. ? ? ? cout?<<?"~Base()"?<<?endl;??
  6. ? ? ?}??
  7. };??

[cpp]?view plaincopy
  1. class?Derived1?:?public?Base?{??
  2. ?public:??
  3. ??Derived1():name_(new?string("NULL"))?{} ?//通過new創建在堆上的對象
  4. ??Derived1(const?string&?n):name_(new?string(n))?{}??
  5. ??
  6. ??~Derived1()?{??
  7. ????delete?name_;??
  8. ????cout?<<?"~Derived1():?name_?has?been?deleted."?<<?endl;??
  9. ??}??
  10. ??
  11. ?private:??
  12. ??string*?name_;??
  13. };??
  14. ??
  15. class?Derived2?:?public?Base?{??
  16. ?public:??
  17. ??Derived2():name_(new?string("NULL"))?{}??
  18. ??Derived2(const?string&?n):name_(new?string(n))?{}??
  19. ??
  20. ??~Derived2()?{??
  21. ????delete?name_;??
  22. ????cout?<<?"~Derived2():?name_?has?been?deleted."?<<?endl;??
  23. ??}??
  24. ??
  25. ?private:??
  26. ??string*?name_;??
  27. };??
我們看下面對其析構情況進行測試:

[cpp]?view plaincopy
  1. int?main()?{??
  2. ??Derived1*?d1?=?new?Derived1(); //d1為Derived1類的指針,它指向一個在堆上創建的Derived1的對象,需要delete調用?
  3. ??Derived2?d2?=?Derived2("Bob"); ?//d2為一個在棧上創建的對象,執行結束后,自動調用析構
  4. ??delete?d1;??
  5. ??return?0;??
  6. }??
d1為Derived1類的指針,它指向一個在堆上創建的Derived1的對象;d2為一個在棧上創建的對象。其中d1所指的對象需要我們顯式的用delete調用其析構函數;d2對象在其生命周期結束時,系統會自動調用其析構函數。看下其運行結果:

剛才我們說,Base基類的析構函數并不是虛析構函數,現在結果顯示,派生類的析構函數被調用了,正常的釋放了其申請的內存資源。這兩者并不矛盾,因為無論是d1還是d2,兩者都屬于靜態綁定,而且其靜態類型恰好都是派生類,因此,在析構的時候,即使基類的析構函數為非虛析構函數,也會調用相應派生類的析構函數。

下面我們來看下,當發生動態綁定時,也就是當用基類指針指向派生類,這時候采用delete顯式刪除指針所指對象時,如果Base基類的析構函數沒有virtual,會發生什么情況?

[cpp]?view plaincopy
  1. int?main()?{??
  2. ??Base*?base[2]?=?{ ?new?Derived1(), new?Derived2("Bob") }; ?
  3. ??for?(int?i?=?0;?i?!=?2;?++i)?{??
  4. ????delete?base[i];??????
  5. ??}??
  6. ??return?0;??
  7. }??

? ? ? ? 從上面結果我們看到,盡管派生類中定義了析構函數來釋放其申請的資源,但是并沒有得到調用。原因是基類指針指向了派生類對象,而基類中的析構函數卻是非virtual的,之前講過,虛函數是動態綁定的基礎。現在析構函數不是virtual的,因此不會發生動態綁定,而是靜態綁定,指針的靜態類型為基類指針,因此在delete時候只會調用基類的析構函數,而不會調用派生類的析構函數。這樣,在派生類中申請的資源就不會得到釋放,就會造成內存泄漏,這是相當危險的:如果系統中有大量的派生類對象被這樣創建和銷毀,就會有內存不斷的泄漏,久而久之,系統就會因為缺少內存而崩潰。

? ? ? ? 也就是說,在基類的析構函數為非虛析構函數的時候,并不一定會造成內存泄漏;當派生類對象的析構函數中有內存需要收回,并且在編程過程中采用了基類指針指向派生類對象,如為了實現多態,并且通過基類指針將該對象銷毀,這時,就會因為基類的析構函數為非虛析構函數而不觸發動態綁定,從而沒有調用派生類的析構函數而導致內存泄漏。

? ? ? ? 因此,為了防止這種情況下內存泄漏的發生,最好將基類的析構函數寫成virtual虛析構函數。

下面把Base基類的析構函數改為虛析構函數:

[cpp]?view plaincopy
  1. class?Base?{??
  2. ?public:??
  3. virtual?~Base()?{??
  4. ??cout?<<?"~Base()"?<<?endl;??
  5. }??
  6. };??
再看下其運行結果:


這樣就會實現動態綁定,派生類的析構函數就會得到調用,從而避免了內存泄漏。


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/458354.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/458354.shtml
英文地址,請注明出處:http://en.pswp.cn/news/458354.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

第二章 Python基本元素:數字、字符串和變量

Python有哪些內置的數據類型&#xff1a; True False #布爾型 42 100000000 #整型 3.14159 1.0e8 #浮點型 abcdes #字符串 2.1 變量、名字和對象 python中統一的形式是什么&#xff1f; 對象&#xff0c;所有的對象都是以對象的形式存在…

Mac - 設置NSButton 的背景色

- (void)drawRect:(NSRect)dirtyRect {[super drawRect:dirtyRect];[[NSColor clearColor] setFill];NSRectFill(self.bounds);self.wantsLayer YES;self.layer.cornerRadius 8;self.layer.masksToBounds YES; } 轉載于:https://www.cnblogs.com/741162830qq/p/5157046.html…

C++中static關鍵字作用總結

from&#xff1a;https://www.cnblogs.com/songdanzju/p/7422380.html1.先來介紹它的第一條也是最重要的一條&#xff1a;隱藏。&#xff08;static函數&#xff0c;static變量均可&#xff09; 當同時編譯多個文件時&#xff0c;所有未加static前綴的全局變量和函數都具有全局…

C Primer Plus 第7章 C控制語句:分支和跳轉 7.4 一個統計字數的程序

2019獨角獸企業重金招聘Python工程師標準>>> 首先&#xff0c;這個程序應該逐個讀取字符&#xff0c;并且應該有些方法判斷何時停止&#xff1b;第二&#xff0c;它應該能夠識別并統計下列單位&#xff1a;字符、行和單詞。下面是偽代碼描述&#xff1a; read a cha…

深入理解extern用法

from&#xff1a;https://blog.csdn.net/z702143700/article/details/46805241一、 extern做變量聲明 l 聲明extern關鍵字的全局變量和函數可以使得它們能夠跨文件被訪問。 我們一般把所有的全局變量和全局函數的實現都放在一個*.cpp文件里面&#xff0c;然后用一個同名的*.h文…

收集整理的非常有用的PHP函數

為什么80%的碼農都做不了架構師&#xff1f;>>> 1、PHP加密解密 2、PHP生成隨機字符串 3、PHP獲取文件擴展名&#xff08;后綴&#xff09; 4、PHP獲取文件大小并格式化 5、PHP替換標簽字符 6、PHP列出目錄下的文件名 7、PHP獲取當前頁面URL 8、PHP強制下載文件 9、…

進程間的通信方式——pipe(管道)

from&#xff1a;https://blog.csdn.net/skyroben/article/details/715133851.進程間通信每個進程各自有不同的用戶地址空間,任何一個進程的全局變量在另一個進程中都看不到&#xff0c;所以進程之間要交換數據必須通過內核,在內核中開辟一塊緩沖區,進程A把數據從用戶空間拷到內…

bash中(),{},(()),[],[[]]的區別

前言:在bash中遇到各種括號&#xff0c;同時在進行字符數值比較判定時&#xff0c;總是不斷出現問題&#xff0c;于是通過參考《advanced bash-scripting guide》&#xff0c;同時在centos 6.7版本上進行測試&#xff0c;現況總結如下。如有紕漏&#xff0c;望指正。一.()一個命…

多進程和多線程之間的通信方式及通信實現步驟小結

進程間通信方式 # 管道( pipe )&#xff1a;管道是一種半雙工的通信方式&#xff0c;數據只能單向流動&#xff0c;而且只能在具有親緣關系的進程間使用。進程的親緣關系通常是指父子進程關系。 # 有名管道 (namedpipe) &#xff1a; 有名管道也是半雙工的通信方式&#xff0c;…

highcharts 顯示網格

2019獨角獸企業重金招聘Python工程師標準>>> xAxis: { gridLineColor: #197F07, gridLineWidth: 1 }, yAxis: { gridLineColor: #197F07, gridLineWidth: 2 }, 轉載于:https://my.oschina.net/LingBlog/blog/697885

Cheat—— 給Linux初學者和管理員一個終極命令行備忘單

編譯自&#xff1a;http://www.tecmint.com/cheat-command-line-cheat-sheet-for-linux-users/作者&#xff1a; Avishek Kumar原創&#xff1a;LCTT https://linux.cn/article-3760-1.html譯者&#xff1a; su-kaiyao原文稍有改動 當你不確定你所運行的命令&#xff0c;尤其是…

串口操作之API篇 CreateFile

CreateFile http://bbs.fishc.com/thread-72944-1-1.html(出處: 魚C論壇) ------------------------------------------------------------------------CreateFile用于打開串口,如果操作成功,返回一個句柄.1 function CreateFile(lpFileName: PChar; dwDesiredAccess, dwShareM…

云數據庫·ApsaraDB 產品6月刊

【重點關注】RDS發布新規格 RDS于5月下旬發布新產品規格&#xff0c;新規格對齊ECS配置:1.連接數大幅提升 互聯網型的應用特點是發展快速&#xff0c;在云上應用層會基于VM進行橫向擴展&#xff0c;對數據庫的要求除了資…

【同行說技術】教你玩轉iOS的5篇技術干貨

在文章《iOS從小白到大神必讀資料匯總一到四》這個系列中&#xff0c;深入介紹了iOS入門學習及進階的相關技術資料&#xff0c;今天小編繼續發布iOS學習的5篇干貨文章&#xff0c;趕緊來看看吧 &#xff01;喜歡寫博客的工程師博主可以加工程師博主交流群&#xff1a;391519124…

Qt Console Application 與 Qt GUI Application互轉

在桌面開發中&#xff0c;總的來說&#xff0c;包含兩種類型的應用程序&#xff1a;無界面的Console程序和有界面的GUI程序。Qt也不例外&#xff0c;包含Qt Console Application和Qt GUI Application。一、Qt Console Application在VS2015中創建一個Qt Console Application&…

Create Volume 操作(Part I) - 每天5分鐘玩轉 OpenStack(50)

2019獨角獸企業重金招聘Python工程師標準>>> 前面已經學習了 Cinder 的架構和相關組件&#xff0c;從本節我們開始詳細分析 Cinder 的各種操作&#xff0c;首先討論 Cinder 如何創建 volume。 Create 操作流程如下&#xff1a; 客戶&#xff08;可以是 OpenStack 最…

如何有效解決C與C++的相互調用問題

from&#xff1a;https://blog.csdn.net/gobitan/article/details/1532769在實際工作中可能經常要進行C和C的混合編程&#xff0c;C調用C語言的代碼通常都比較容易&#xff0c;但也有一些細節需要注意。C要調用C的代碼就略為麻煩一些&#xff0c;因為C不支持面向對象的特征。一…

Eclipse開發工具之崩潰和備份

1.通過在命令行中輸入“where java”&#xff0c;找到除jdk目錄下的所有java相關程序&#xff0c;直接刪掉&#xff08;一般會在C:WINDOWSsystem32下&#xff09;以后再也不用怕找不到目錄了 2.內存不足&#xff0c;打開Eclipse目錄下的eclipse.ini&#xff0c;把里面的-Xmx512…

IOS-網絡(監聽網絡狀態)

1 //2 // BWNetWorkTool.h3 // IOS_0131_檢測網絡狀態4 //5 // Created by ma c on 16/1/31.6 // Copyright © 2016年 博文科技. All rights reserved.7 //8 9 #import <Foundation/Foundation.h> 10 11 interface BWNetWorkTool : NSObject 12 ///是否是WiFi …

C++中的friend詳細解析

C中的友元機制允許類的非公有成員被一個類或者函數訪問&#xff0c;友元按類型分為三種&#xff1a;普通非類成員函數作為友元,類的成員函數作為友元&#xff0c;類作為友元。友元包括友元的聲明以及友元的定義。 友元的聲明默認為了extern&#xff0c;就是說友元類或者友元函數…