More Effective C++ 35個改善編程與設計的有效方法筆記與心得 3

三. 異常

條款9: 利用destructors避免泄露資源

????  在編程中,"資源"可以指任何系統級的有限資源,如內存文件句柄網絡套接字等。"泄露"則是指在應用程序中分配了資源,但在不再需要這些資源時沒有正確地釋放它們。這種狀況常常會導致資源過度使用,如內存溢出,這個問題在長時間運行的程序中尤其嚴重。

????  而"利用destructors避免泄露資源"則是一種建議。在C++中,析構函數(Destructor) 是一種特殊的成員函數,它會在每次刪除所創建的對象時執行。

????  匆忙地,可以在析構函數中清理和釋放資源,這樣當對象的生命周期結束時,析構函數會自動調用,阻止資源的泄漏。這是一種被稱作RAII(Resource Acquisition is Initialization)的編程技術。

????  舉例來說,如果你的類負責管理一個動態分配的內存塊,那你可以在析構函數中使用?delete?來釋放這個內存塊。這樣只要對象存在,你就可以確保它持有的資源會在合適的時機被釋放。

????  所以,"利用destructors避免泄露資源"的意思是:在設計類時,應該充分利用構造函數和析構函數來管理資源,以防止任何可能的資源泄漏。

請記住
????  只要堅持這個規則,把資源封裝在對象內,通常便可以在exceptions出現時避免內存泄漏。

條款10: 在constructors內阻止資源泄露(resource leak)

????  "在constructors內阻止資源泄露"這句話的意思是,在設計類的構造函數時需要以一種防止資源泄露(如內存泄露)的方式管理資源

????  我們通常在構造函數中獲取需要的資源,并在析構函數中釋放這些資源,這是一種常用的RAII(資源獲取即初始化) 技術。但是,如果在獲取資源后和資源釋放之間發生異常,可能會導致資源泄漏

????  以內存申請為例,如果在構造函數中申請了內存,但后續程序出現異常,且這個異常沒有被捕獲,那么程序可能在沒有執行析構函數(也就是沒有釋放內存)的情況下終止,從而導致內存泄露

????  為了避免這種情況,你可以在構造函數中使用try-catch塊來捕獲異常,一旦拋出異常,就清理申請的資源,再重新拋出異常。或者利用一些智能指針(如unique_ptr、shared_ptr等),它們在析構時可以自動釋放資源,可用來避免資源泄漏。這樣做,就可以確保在構造函數執行過程中,無論是否發生異常,申請的資源都能被妥善處理,從而避免資源泄露。

條款11: 禁止異常(exceptions)流出destructors之外

????  "禁止異常(exceptions)流出destructors之外"這句話的含義是:在編寫析構函數(destructors)時,應確保任何可能拋出的異常都被妥善處理,不允許它們傳播(或“流出”)到析構函數的外部。

????  這是一個重要的編程原則,因為在析構函數中允許異常傳播可能會引發各種復雜的問題。例如,如果析構函數在清理資源時拋出了一個異常,但是這個異常在外部沒有被捕獲,那么這個異常會導致程序猝死(即異常終止)。更糟糕的是,如果析構函數是在處理另一個異常時被調用的(比如在堆棧展開過程中),而它又拋出了第二個異常,那么程序會立即被終止。

????  因此,良好的做法是使析構函數只做它該做的事情,即清理資源,而且要確保它能正確執行,不會拋出異常。在多數情況下,析構函數不應該進行可能會拋出異常的操作。如果無法避免,那么就應該在析構函數內部處理可能拋出的異常,確保它們不會流到析構函數的外部。這通常可以通過添加一個try-catch塊來實現。

條款12: 了解“拋出一個exception”與“傳遞一個參數”或“調用一個虛函數”之間的差異

主要存在三個差異

  1. exception objects總是會被復制,如果以by value方式捕捉,他們甚至被復制兩次。至于傳遞給函數參數的對象則不一定得復制。
  2. “被拋出成為exception”的對象,其被允許的類型轉換動作,比“被傳遞到函數去”的對象少
  3. catch子句以其“出現于源代碼順序”被編譯器檢驗對比,其中第一個匹配成功者便被執行;而當我們以某對象調用一個虛函數,被選中執行的是哪那個“與對象類型最佳吻合”的函數,不論它是不是源代碼所列的第一個。

解釋
????  “拋出一個exception”,"傳遞一個參數"以及"調用一個虛函數"都是編程中常見的操作,不過它們在功能和目標上有著顯著的差異。

????  首先,"拋出一個exception"是一種特殊的程序流控制機制,用于處理程序運行時的異常情況。當程序運行到一個錯誤狀態,無法正常執行時,就可以拋出(throw)一個異常。這將立即中斷當前函數的執行,將控制權轉移到最近的可以處理該異常的異常處理程序(catch語句)。異常是一種用于處理程序錯誤的強大工具,但是如果過度使用,可能導致代碼邏輯難以理解和維護。

????  其次,"傳遞一個參數"是調用函數時的一種操作。通過參數,我們可以將數據從函數調用者傳遞到被調用的函數中。參數可以是任何類型的數據,如整數、浮點數、字符串、對象等。

????  最后,"調用一個虛函數"是面向對象編程中的一個概念,出現在如C++這樣支持多態性(Polymorphism)的語言中。通過在基類中聲明虛函數,派生類可以根據需要覆寫(Override)這個函數。在運行時,通過基類指針或引用調用虛函數,會根據實際的對象類型來決定調用哪個版本的虛函數。這是實現運行時多態性的關鍵。

????  總的來說,“拋出一個exception”通常用于處理錯誤,“傳遞一個參數”則是在函數之間傳遞數據,“調用一個虛函數”是實現多態性的手段。這三者都是編程中重要的概念,且在使用時目標和上下文有非常大的差異。

針對上面的三點主要差異,這里做一下解釋說明

  1. 這里的表述涉及到了在C++中對異常(exception)的處理方式以及函數參數的傳遞方式。

????  首先,在C++中當你拋出一個異常對象時,這個對象會被復制。系統從throw語句開始,搜索這個異常匹配的catch塊,這個過程被稱為異常傳播。在這個過程中,原始異常對象會被拷貝一次。

????  然后,當異常被catch塊捕獲時,如果以by value方式捕捉,即’catch (Exception e)',參數e是原始異常的副本,這樣就會發生第二次復制。因此,通過by value的方式捕獲異常將導致異常對象被復制兩次。

????  另一方面,函數參數的傳遞方式可以有兩種,一種是by value,一種是by reference。當我們通過by value方式傳遞參數時,和catch塊捕捉異常一樣,參數也會被復制。但是,如果我們選擇通過by reference形式傳遞,例如’void function(Exception& e)',則不會發生復制,參數e是原始異常對象的引用。

????  所以這句話的意思是:異常對象會在傳播過程中被復制,如果通過by value方式被捕獲,還會發生一次復制。而函數參數是否復制則取決于參數的傳遞方式。如果參數是通過by value方式傳遞,就會復制,如果是通過by reference方式傳遞,就不會復制。

  1. 這段話的含義是,在C++編程中,當一個對象被拋出為一個異常(exception)時,它可以進行的類型轉換比作為函數參數傳遞的對象要少。

????  當一個對象被拋出為異常時,僅允許以下類型轉換:

  1. 到基類的轉換:如果拋出的是一個派生類對象,catch塊可以捕獲基類的異常。
  2. 通過構造函數定義的轉換:如果存在一個構造函數,其唯一參數是異常的源類型或者源類型可以轉換為這個參數類型。

????  然而,當一個對象作為函數參數被傳遞時,除了上述的轉換之外,還允許以下幾種轉換:

  1. 提升(Promotion):比如將整數字面量提升為整數、將字符提升為整數等。
  2. 標準類型轉換:如將整數轉為浮點數或者相反,將指針轉為布爾值等。
  3. 用戶定義的轉換,例如通過轉換函數進行類型轉換。

????  因此,“被拋出成為exception”的對象,其被允許的類型轉換動作,比“被傳遞到函數去”的對象要少。

  1. 這段話對catch子句的處理方式和虛函數的調用進行了比較,并強調了兩者在選擇執行方式上的不同。

????  catch子句以其"出現于源代碼順序"被編譯器檢驗對比,其中第一個匹配成功者便被執行:
????  這部分是對C++異常處理機制的描述。當一個異常被拋出時,編譯器會按照catch子句在源代碼中出現的順序逐一檢驗這個異常是否匹配。一旦找到第一個匹配的catch子句,就會執行這個子句中的代碼。這就解釋了為什么我們需要從最詳盡的異常類型開始寫catch子句,再逐漸寫出越來越一般的類型,因為一旦找到匹配的子句,就不會再繼續查找。

????  當我們以某對象調用一個虛函數,被選中執行的是哪那個“與對象類型最佳吻合”的函數,不論它是不是源代碼所列的第一個: 這部分是在描繪多態性在面向對象編程中的應用。在C++里,當我們通過一個基類指針或引用來調用派生類的虛函數時,實際上執行的是派生類版本的函數,即使在源代碼中,這個虛函數的聲明可能并不是第一個出現的。這是因為在編譯和運行時,編譯器和運行環境會選擇那個與對象實際類型最符的函數來執行,這種功能稱為多態。

????  通過對比,這段話強調了catch子句和虛函數在執行選擇上的區別:catch子句按照源代碼順序選擇,而虛函數則是根據對象的實際類型來決定。

條款13: 以by reference方式捕捉exception

請記住
????  catch by reference,就可以避開對象刪除問題;可以避開exception objects的切割問題;約束了exception objects需要被復制的次數

解釋
????  在C++中,我們可以用by reference方式來捕捉異常。這就意味著我們會直接使用到原本拋出的異常對象,而不是它的副本。

????  C++里的異常處理機制中,當我們使用“catch(異常類型 & 引用)” 的方式來捕獲異常,這就是以by reference方式捕捉異常。按照這種方式,系統只需要生成一次異常對象副本,即在拋出異常時,不需要在 catch 子句中再復制一次。這對于大對象或者復制對象資源消耗大的情況下,是很有用的策略。

try {// 代碼塊,可能拋出異常throw MyException();
}
catch (MyException& e) {// 這里捕獲的 e 是一個引用, 會直接影響到原本的異常對象// 對 e 的改變也會影響到原先拋出的 MyException 對象
}

????  此外,采用這種方式捕捉異常還可以解決一些多態問題。如果有一些異常類通過繼承關系鏈接,捕捉父類異常引用可以接到子類的異常。這是由于引用可以保持多態性,允許我們通過基類引用來訪問派生類中的重寫虛函數。而拷貝的方式會切除對象的派生部分,帶來切割問題(slicing problem)。

????  “切割問題”(slicing problem),源于C++對于對象的處理方式,特別是在處理對象副本和繼承關系時常常遇到。

????  切割問題通常發生在當你有一個派生類(derived class)對象,并且你試圖將其拷貝到一個基類(base class)對象時。在這個過程中,派生類中的所有附加信息都會被“切割”掉,只剩下基類部分。這是因為基類只能容納基類的成員,對應的派生類的成員對其來說是未知的,復制過程中會丟失這部分信息,這就是所謂的切割問題。

????  當我們處理異常時,如果用基類類型去捕獲派生類的異常(這在面向對象設計中是很常見的情況,我們希望用同一段處理代碼來處理一類相關的異常),如果我們是按值捕獲的,就會發生切割問題,所有的異常都會被處理成基類類型的異常。

????  但如果我們使用引用方式捕獲異常,就可以避免切割問題。因為引用方式不涉及對象的復制,它直接引用到了原始的異常對象,保持了對象的完整性,包括其真正的類型。這樣基于類型的動態綁定仍然能夠正確工作,我們能夠捕獲并處理正確的異常類型。

????  所以,“catch by reference可以避開exception objects的切割問題”這句話的意思就是,通過引用的方式捕獲異常可以避免丟失原始異常類型的信息,從而避免了異常切割的問題。

條款14: 明智運用exception sepcifications

????  “明智運用exception specifications” 這句話關注的是如何適當地使用C++中的異常規范(exception specifications)
????  
????  在C++中,異常規范是一種語法,允許函數說明它可能拋出的異常類型。這是在函數聲明的尾部,通過"throw(異常類型列表)"來進行說明的。比如,一個函數可能這樣聲明:“void foo() throw(A, B);”,表示foo()函數可能會拋出類型A和B的異常。如果函數聲明時未指定throw列表,或者這個列表為空,那么函數就可以拋出任何類型的異常,或不拋出異常。

????  然而,在C++11以后,異常規范這一設定已經被廢棄,不再建議使用。取而代之的是noexcept關鍵字,用來指示函數是否可能拋出異常。
????  
????  在"明智運用exception specifications"這個表述中,"明智"兩字暗示了要根據具體情況和需求謹慎使用這類規范,避免濫用。過度使用異常規范可能引起不必要的復雜性,并可能對性能產生影響。另外,也需要注意隨著C++標準的更新,異常規范的使用情況也在發生變化。所以,在設計和編寫代碼時,需要密切關注最新的實踐和建議,明智地運用這些規范。

條款15: 了解異常處理(exception handing)的成本

解釋
????  "了解異常處理(exception handing)的成本"這句話是在說我們應該了解在我們的代碼中使用異常處理機制帶來的開銷。

????  在大多數編程語言中,異常處理都有自己的開銷。這里的"開銷"主要包括兩部分:時間開銷和空間開銷。

  1. 時間開銷:當一個異常被拋出時,需要在調用棧上回溯查找匹配的catch塊,這個過程需要時間。另外,創建異常對象、復制異常對象到catch塊(如果不是以引用方式捕獲)以及析構對象都是需要時間的。

  2. 空間開銷:異常處理機制需要在內存中保存足夠的信息,以便在異常發生時可以正確地解 unwinding 調用棧。這包括異常對象本身的空間,以及可能需要通過棧回溯的信息。

????  通常來說,如果異常不頻繁發生,這些開銷可能不大。但是在一些性能臨界的應用中,這些成本可能就相當重要了。

????  了解這些異常處理的成本,可以幫助我們更好地在性能和可讀性、易用性之間做出平衡。例如,一般來說,我們推薦在異常是真正"異常"的情況下使用異常機制,比如函數預計不會失敗,但卻由于某些無法預計的原因比如內存耗盡而失敗。對于預期可能會失敗的函數,比如文件打開操作,使用返回錯誤碼可能是更好的方式。這樣可以保證性能,但又不犧牲代碼的結構。

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

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

相關文章

Linux 安裝 Redis 教程

優質博文:IT-BLOG-CN 一、準備工作 配置gcc:安裝Redis前需要配置gcc: yum install gcc如果配置gcc出現依賴包問題,在安裝時提示需要的依賴包版本和本地版本不一致,本地版本過高,出現如下問題&#xff1a…

Jupyter無法導入庫,但能在終端導入的問題

Jupyter無法導入庫,但能在終端導入 ?錯誤問題描述:conda activate LLMs激活某個Conda的環境后,盡管已經通過conda或者pip在這個環境中安裝了一些🐍Python的庫,但無法在Jupyter中導入,卻能在終端成功導入。…

京東商品詳情數據接口(JD.item_get)丨京東API實時接口指南

京東商品詳情API接口(JD.item_get)是京東開放平臺提供的一個數據接口,用于獲取京東平臺上單個商品的詳細信息。 通過這個接口,開發者可以獲取到包括商品名稱、品牌、產地、規格參數、價格信息、銷量、評價、圖片、描述等在內的詳…

Node.js開發實戰 視頻教程 下載

ode.js開發實戰 視頻教程 下載 下載地址 https://download.csdn.net/download/m0_67912929/89487510 01-課程介紹.mp4 02-內容綜述.mp4 03-Node.js是什么? .mp4 04-Node.js可以用來做什么?.mp4 05-課程實戰項目介紹.mp4 06-什么是技術預研? .mp4 07-Node.js開發環境…

Windows 11 安裝 安卓子系統 (WSA)

How to Install Windows Subsystem for Android (WSA) on Windows 11 新手教程:如何安裝Windows 11 安卓子系統 說明 Windows Subsystem for Android 或 WSA 是由 Hyper-V 提供支持的虛擬機,可在 Windows 11 操作系統上運行 Android 應用程序。雖然它需…

【JS】注意考點

1.聲明變量時所遵循的規則: (1)可以使用一個保留關鍵字var同時聲明多個變量 (2)可以在聲明變量的同時對其賦值, (3)如果只是聲明了變量,并未對其賦值,其值就默認為 Undefined。 (4)保留關鍵字var可以用作for語句和for…in語句…

python基礎_類

在Python中,類(Class)是面向對象編程(OOP)的核心概念之一。類提供了一種創建新對象的模板,這些對象通常被稱為類的實例或對象。以下是關于Python類的一些關鍵點和特性: 定義類 類通過class關鍵…

PostgreSQL的系統視圖pg_stat_wal

PostgreSQL的系統視圖pg_stat_wal 在 PostgreSQL 數據庫中,pg_stat_wal 視圖提供了與 WAL(Write-Ahead Logging)日志有關的統計信息。WAL 是 PostgreSQL 用于確保數據一致性和持久性的重要機制。因此,監控和分析 WAL 活動對于數據…

ctfshow-web入門-命令執行(web71-web74)

目錄 1、web71 2、web72 3、web73 4、web74 1、web71 像上一題那樣掃描但是輸出全是問號 查看提示:我們可以結合 exit() 函數執行php代碼讓后面的匹配緩沖區不執行直接退出。 payload: cvar_export(scandir(/));exit(); 同理讀取 flag.txt cinclud…

文華財經博易大師盤立方多空波段止損畫線指標公式

TT:PERIOD7; EMA120:EMA(C,120); RSV:(CLOSE-LLV(LOW,9))/(HHV(HIGH,9)-LLV(LOW,9))*100; K:SMA(RSV,3,1); D:SMA(K,3,1); J:3*K-2*D; DRAWTEXT(TT&&J<0,L,多),VALIGN0; DRAWTEXT(TT&&J>100,H,空),VALIGN2; IF(TT,EMA(C,60),NULL),RGB(255,255,2…

JavaScript數組對象 , 正則對象 , String對象以及自定義對象介紹

1. Array數組對象 數組對象是使用單獨的變量名來存儲一系列的值。 1.1創建一個數組 創建一個數組&#xff0c;有三種方法。 【1】常規方式: let 數組名 new Array();【2】簡潔方式: 推薦使用 let 數組名 new Array(數值1,數值2,...);【3】字面:在js中創建數組使用中括號…

【ubuntu 】使用samba配置共享用戶home目錄和其他具體路徑

目錄 1 安裝samba 2 修改Samba配置文件 3 增加Rose用戶的samba帳號 4 重啟samba 5 測試 1 安裝samba 使用如下命令安裝samba&#xff1a; sudo apt-get updatesudo apt-get install samba openssh-server 2 修改Samba配置文件 sudo cp /etc/samba/smb.conf /etc/samba…

試用筆記之-收錢吧安卓版演示源代碼,收錢吧手機版感受

首先下載&#xff1a; https://download.csdn.net/download/tjsoft/89499105 安卓手機安裝 如果有收錢吧帳號輸入收錢吧帳號和密碼。 如果沒有收錢吧帳號點我的注冊 登錄收錢吧帳號后就可以把手機當成收錢吧POS機用了&#xff0c;還可以掃客服的付款碼哦 源代碼技術交流QQ:42…

Docker安裝MySQL5

Docker安裝MySQL5 前言 MySQL 是一個開源的關系型數據庫管理系統&#xff0c;廣泛用于各種 Web 應用程序的開發和生產環境中。MySQL 5 是 MySQL 數據庫的一個較早版本&#xff0c;雖然不再是最新版本&#xff0c;但仍然被一些項目所使用和支持。 在 Docker 中安裝 MySQL 5 可…

Docker 手冊

幫助命令 docker 命令 --help鏡像命令 docker images (-a所有 &#xff5c; -q只顯示容器的ID) docker search 鏡像名 docker pull 鏡像名&#xff1a;版本號 docker rmi -f ID&#xff5c;鏡像名&#xff1a;版本號 // 刪除本地一個或多個鏡像 docker rmi -f $(docker …

U盤數據恢復實戰指南:原因、方案與預防措施

一、引言&#xff1a;U盤數據恢復概述 在數字化時代&#xff0c;U盤作為一種便攜式存儲設備&#xff0c;廣泛應用于個人和企業中。然而&#xff0c;由于各種原因&#xff0c;U盤數據丟失的問題時有發生。U盤數據恢復技術便是在這種情況下應運而生&#xff0c;它幫助用戶在數據…

TPS61085非同步650kHz,1.2MHz, 18.5V升壓DCDC芯片

1 特點 TPS61085外觀和絲印PMKI 2.3 V 至 6 V 輸入電壓范圍 具有 2.0A 開關電流的 18.5V 升壓轉換器 650kHz/1.2MHz 可選開關頻率 可調軟啟動 熱關斷 欠壓閉鎖 8引腳VSSOP封裝 8引腳TSSOP封裝 2 應用 手持設備 GPS接收器 數碼相機 便攜式應用 DSL調制解調器 PCMCIA卡 TFT LCD…

MySQL之事務特性ACID、并發事務問題、隔離級別

1、事務特性ACID 原子性&#xff1a;事務是不可分割的最小操作單元&#xff0c;要么全部成功&#xff0c;要么全部失敗。一致性&#xff1a;事務完成時&#xff0c;必須使所有數據都保持一致狀態。隔離性&#xff1a;數據庫系統提供的隔離機制&#xff0c;保證事務在不外部并發…

Java并發編程基礎知識點

目錄 Java并發編程基礎知識點1、線程&#xff0c;進程概念及二者的關系進程相關概念線程相關概念進程與線程的關系補充小知識點&#xff1a; 2、線程的狀態Java線程的狀態&#xff1a;Java線程不同狀態之間的切換圖示 3、Java程序中如何創建線程&#xff1f;①、繼承Thread類②…

2024年華為OD機試真題-傳遞悄悄話 -C++-OD統一考試(C卷D卷)

2024年OD統一考試(D卷)完整題庫:華為OD機試2024年最新題庫(Python、JAVA、C++合集) 題目描述: 給定一個二叉樹,每個節點上站著一個人,節點數字表示父節點到該節點傳遞悄悄話需要花費的時間。 初始時,根節點所在位置的人有一個悄悄話想要傳遞給其他人,求二叉樹所有節…