【C++】:異常

目錄

C語言處理錯誤的方式

C++異常的概念

C++異常的使用

異常的拋出與捕獲匹配原則

函數調用鏈中的棧展開?

異常重新拋出?

異常安全

異常規范

標準庫異常體系

自定義異常體系

異常的優缺點


C語言處理錯誤的方式

  1. 返回值檢查:函數返回特定錯誤碼或值標識失敗,但需逐層檢查且易被忽略。

  2. 全局變量?errno:依賴全局變量記錄錯誤類型,存在線程安全隱患和覆蓋風險。

  3. 斷言(Assert):通過斷言驗證邏輯假設,但僅適用于調試且失敗直接終止程序。

  4. 非局部跳轉(setjmp/longjmp):支持跨函數錯誤跳轉,但易導致資源泄漏和代碼混亂。

  5. 信號處理:捕獲系統信號處理嚴重錯誤,但處理函數功能受限且不可靠。

  6. Goto清理:集中釋放資源避免冗余,但濫用會破壞代碼結構化邏輯。

C++異常的概念

C++ 異常處理是一種用于管理程序運行時錯誤的機制,它通過分離錯誤處理代碼和正常邏輯來提高代碼的可維護性。?

  • try:包裹可能拋出異常的代碼塊

  • throw:拋出異常對象(任意類型)

  • catch:捕獲并處理特定類型的異常

try {// 可能拋出異常的代碼if (error) throw MyException("Error occurred");
} 
catch (const MyException& e) {// 處理 MyException 類型異常std::cerr << e.what() << std::endl;
}
catch (...) {  // 捕獲所有異常std::cerr << "Unknown error" << std::endl;
}

C++異常的使用

異常的拋出與捕獲匹配原則

1、類型精確匹配

  • 異常捕獲基于?類型匹配catch?塊按順序嘗試匹配異常類型

  • 被選中的處理代碼(catch塊)是調用鏈中與該對象類型匹配且離拋出異常位置最近的那一個。
  • 異常是通過拋出對象而引發的,該對象的類型決定了應該激活哪個catch的處理代碼,如果拋出的異常對象沒有捕獲,或是沒有匹配類型的捕獲,那么程序會終止報錯。
try {throw 42;  // 拋出 int 類型異常
}
catch (double d) { /* 不會捕獲 */ }
catch (int i) {   // 匹配成功std::cout << "Caught int: " << i;
}

2、繼承體系中的匹配

  • 基類?catch?塊可以捕獲派生類異常(需通過?引用或指針?捕獲避免對象切片)

  • 捕獲和拋出的異常類型并不一定要完全匹配,可以拋出派生類對象,使用基類進行捕獲。
  • 推薦實踐:優先捕獲派生類異常,再捕獲基類

try {throw std::runtime_error("Error");
}
catch (const std::runtime_error& e) {  // 優先匹配具體類型std::cerr << "Runtime error: " << e.what();
}
catch (const std::exception& e) {      // 基類捕獲兜底std::cerr << "Standard exception: " << e.what();
}

3、特殊匹配規則

  • catch (...)?捕獲所有異常(通常用于資源清理),但捕獲后無法知道異常錯誤是什么。

  • const?修飾不影響匹配:catch (std::exception)?與?catch (const std::exception)?視為相同

函數調用鏈中的棧展開?

  • 當異常被拋出后,首先檢查 throw 本身是否在try塊內部,如果在則查找匹配的catch語句,如果有匹配的,則跳到catch的地方進行處理。
  • 如果當前函數棧沒有匹配的 catch 則退出當前函數棧,繼續在上一個調用函數棧中進行查找匹配的catch。找到匹配的catch子句并處理以后,會沿著 catch 子句后面繼續執行,而不會跳回到原來拋異常的地方。
  • 如果到達main函數的棧,依舊沒有找到匹配的catch,則終止程序。
void func3() 
{std::vector<int> localObj(100);  // RAII 對象throw std::runtime_error("Boom"); // 拋出異常// localObj 自動析構
}void func2() { func3(); }  // 異常繼續傳播
void func1() { func2(); }  // 異常繼續傳播int main() 
{try {func1();}catch (const std::exception& e) {std::cerr << "Caught: " << e.what();}return 0;
}
  1. 函數調用鏈

    • main()?調用?func1()

    • func1()?調用?func2()

    • func2()?調用?func3()

  2. 異常拋出(func3)

    • 在?func3()?中,首先構造局部對象?std::vector<int> localObj(100)(RAII管理內存)。

    • 執行?throw std::runtime_error("Boom"),拋出異常,函數執行中斷。

  3. 棧展開(Stack Unwinding)

    • func3 棧幀銷毀localObj?的析構函數自動調用,釋放分配的100個int內存(RAII確保資源釋放)。

    • func2 棧幀銷毀:因無局部對象,直接退出。

    • func1 棧幀銷毀:同理,無資源需清理。

  4. 異常捕獲(main)

    • 異常傳播至?main()?的?try?塊。

    • catch (const std::exception& e)?捕獲異常(std::runtime_error?是?std::exception?的派生類)。

    • 輸出錯誤信息:Caught: Boom

異常重新拋出?

在?catch?塊中使用?throw;?重新拋出當前異常

典型場景

  • 記錄日志后繼續傳播異常

  • 部分處理異常后交由上層處理

異常安全

  1. 構造函數完成對象的構造和初始化,最好不要在構造函數中拋出異常,否則可能導致對象不完整或沒有完全初始化。
  2. 析構函數主要完成對象資源的清理,最好不要在析構函數中拋出異常,否則可能導致資源泄露(內存泄露、句柄未關閉等)。
  3. C++中異常經常會導致資源泄露的問題,比如在new和delete中拋出異常,導致內存泄露,在lock和unlock之間拋出異常導致死鎖,C++經常使用RAII的方式來解決以上問題。

異常規范

1、優先使用?noexcept

  • 適用場景

    • 移動操作、析構函數、內存釋放函數(如?operator delete)。

    • 明確無失敗可能的函數(如數學計算)。

double sqrt(double x) noexcept 
{ // 假設輸入已校驗,不會拋異常return std::sqrt(x); 
}

2. 避免使用動態異常聲明

void oldFunc() throw(std::runtime_error); // C++17 已移除,禁止使用
// 替代方案:通過文檔說明可能拋出的異常類型。

3、注意事項?

  1. 在函數的后面接throw(type1, type2, ...),列出這個函數可能拋擲的所有異常類型。
  2. 在函數的后面接throw()noexcept(C++11),表示該函數不拋異常。
  3. 若無異常接口聲明,則此函數可以拋擲任何類型的異常。(異常接口聲明不是強制的)
// 表示func函數可能會拋出A/B/C/D類型的異常
void func() throw(A, B, C, D);// 表示這個函數只會拋出bad_alloc的異常
void* operator new(std::size_t size) throw(std::bad_alloc);// 表示這個函數不會拋出異常
void* operator new(std::size_t size, void* ptr) throw();

標準庫異常體系

C++ 標準庫提供了一套層次化的異常類體系,所有標準異常均繼承自?std::exception?基類。這些異常類型覆蓋了常見的程序錯誤場景,開發者可以直接使用或繼承它們實現自定義異常。

std::exception
├── std::bad_alloc                // 內存分配失敗(new 失敗)
├── std::bad_cast                 // dynamic_cast 轉換失敗(非多態類型)
├── std::bad_typeid               // typeid 操作符作用于空指針
├── std::ios_base::failure        // I/O 流錯誤(如文件打開失敗)
|
├── std::logic_error              // 程序邏輯錯誤(可預防的)
│   ├── std::invalid_argument     // 無效參數(如參數不符合預期范圍)
│   ├── std::domain_error         // 數學運算定義域錯誤(如對負數取對數)
│   ├── std::length_error         // 超出允許長度(如 vector::reserve 超過 max_size)
│   └── std::out_of_range         // 訪問越界(如 vector::at 越界索引)
|
└── std::runtime_error            // 運行時錯誤(不可預見的)├── std::range_error          // 計算結果超出有效范圍(如浮點數轉換溢出)├── std::overflow_error       // 算術上溢錯誤├── std::underflow_error      // 算術下溢錯誤└── std::system_error         // 系統調用錯誤(含錯誤碼,C++11 引入)
  • exception類的 what成員函數 和 析構函數都定義成了虛函數,方便子類對其進行重寫,從而達到多態的效果。
  • 我們也可以去繼承exception類來實現自己的異常類,但實際中很多公司都會自己定義一套異常繼承體系。

?自定義異常:通過繼承?std::runtime_error?或?std::logic_error?添加額外信息。

#include <stdexcept>
#include <string>class NetworkException : public std::runtime_error 
{int error_code_;
public:NetworkException(int code, const std::string& message): std::runtime_error(message), error_code_(code) {}int getErrorCode() const noexcept { return error_code_; }
};// 使用
throw NetworkException(404, "Service not found");

代碼示例

#include <iostream>
#include <fstream>
#include <stdexcept>
#include <string>void readConfigFile(const std::string& filename) 
{std::ifstream file(filename);if (!file) {throw std::runtime_error("無法打開文件: " + filename);}std::string line;while (std::getline(file, line)) {if (line.empty()) {throw std::invalid_argument("配置文件存在空行");}// 解析配置...}
}int main() 
{try {readConfigFile("settings.conf");}catch (const std::invalid_argument& e) {std::cerr << "參數錯誤: " << e.what() << std::endl;}catch (const std::runtime_error& e) {std::cerr << "運行時錯誤: " << e.what() << std::endl;}catch (const std::exception& e) {std::cerr << "標準異常: " << e.what() << std::endl;}return 0;
}

自定義異常體系

實際中很多公司都會自定義自己的異常體系進行規范的異常管理。

  • 公司中的項目一般會進行模塊劃分,讓不同的程序員或小組完成不同的模塊,如果不對拋異常這件事進行規范,那么負責最外層捕獲異常的程序員就非常難受了,因為他需要捕獲大家拋出的各種類型的異常對象。
  • 因此實際中都會定義一套繼承的規范體系,先定義一個最基礎的異常類,所有人拋出的異常對象都必須是繼承于該異常類的派生類對象,因為異常語法規定可以用基類捕獲拋出的派生類對象,因此最外層就只需捕獲基類就行了。

一、為何需要自定義異常?

  1. 錯誤分類:為特定領域(如文件I/O、網絡、數據庫)定義明確的錯誤類型。

  2. 攜帶額外信息:在異常對象中封裝錯誤碼、文件名、操作步驟等上下文信息。

  3. 統一接口:繼承自?std::exception,兼容標準異常處理邏輯。

二、設計原則

  1. 繼承標準異常:所有自定義異常應直接或間接繼承?std::exception

  2. 層次化結構:按錯誤類型分層(如?NetworkException?派生出?TimeoutException)。

  3. 支持多態:通過虛函數(如?what())提供統一的錯誤信息接口。

  4. 異常安全:確保自定義異常類的構造函數和成員函數不拋出異常。

三、實現步驟

1. 基類設計(兼容標準異常)?

#include <exception>
#include <string>class Exception
{
public:// 構造函數(允許傳入錯誤描述)Exception(int errid, const char* errmsg):_errid(errid), _errmsg(errmsg){}int GetErrid() const{return _errid;}// 重寫 what(),返回錯誤信息virtual string what() const{return _errmsg;}
protected:int _errid;     //錯誤編號string _errmsg; //錯誤描述//...
};

2. 派生具體異常類

// 文件操作異常
class FileIOException : public Exception 
{
public:explicit FileIOException(const std::string& filename, const std::string& action): Exception("File Error: Failed to " + action + " file '" + filename + "'") {}
};// 網絡超時異常
class NetworkTimeoutException : public Exception 
{
public:NetworkTimeoutException(const std::string& url, int timeout_sec): Exception("Network Timeout: Request to '" + url + "' timed out after " + std::to_string(timeout_sec) + " seconds") {}
};

?3.?使用自定義異常

void readFile(const std::string& filename) 
{std::ifstream file(filename);if (!file.is_open()) {// 拋出異常throw FileIOException(filename, "open");}// 文件操作...
}try 
{readFile("config.yaml");
} 
catch (const FileIOException& e) 
{std::cerr << "文件操作失敗: " << e.what() << std::endl;// 嘗試恢復或重試
}
catch (const MyBaseException& e) 
{std::cerr << "通用錯誤: " << e.what() << std::endl;
}

異常的優缺點

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

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

相關文章

SZU軟件工程大學生涯 2022~2026

用于個人面試前自我介紹&#xff0c;防止忘記或談吐不流利。 面試官您好&#xff0c;我是來自深圳大學計算機與軟件學院的軟件工程專業的王雅賢。在校期間&#xff0c;我修讀了程序設計基礎、面向對象程序設計、數據結構、算法分析與設計、操作系統等核心課程&#xff0c;系統…

【JavaWeb學習Day27】

Tlias前端 員工管理 條件分頁查詢&#xff1a; 頁面布局 搜索欄&#xff1a; <!-- 搜索欄 --><div class"container"><el-form :inline"true" :model"searchEmp" class"demo-form-inline"><el-form-item label…

Linux 系統運行 Android 應用的幾種方案

這幾年&#xff0c;國產操作系統替代正在有條不紊地進行中。但生態是繞不過去的一道坎&#xff0c;指望應用廠商一下子完成國產系統適配也不現實。之前介紹過使用 Wine 運行 Windows 應用的方案&#xff0c;減少了國產系統應用偏少的難題。比如我在辦公室使用最多的企業微信&am…

Python進階教程丨lambda函數

1. lambda函數是什么&#xff1f; 在 Python 里&#xff0c;lambda 函數是一種特殊類型的函數&#xff0c;也被叫做匿名函數。匿名”意味著它不需要像常規函數那樣使用 def 來進行命名。lambda lambda 函數本質上是簡潔的臨時函數 &#xff0c;它適用于只需要簡單邏輯的場景&a…

TK矩陣系統:高效管理與智能化操作平臺

隨著TikTok等社交媒體平臺的快速發展&#xff0c;短視頻創作和內容運營逐漸成為互聯網行業的重要組成部分。為了幫助內容創作者、品牌運營商以及數據分析人員更高效地管理多個TikTok賬號并優化運營策略&#xff0c;TK矩陣系統提供了一種全新的解決方案&#xff0c;結合了先進的…

Spring Boot整合Apache BookKeeper教程

精心整理了最新的面試資料和簡歷模板&#xff0c;有需要的可以自行獲取 點擊前往百度網盤獲取 點擊前往夸克網盤獲取 Spring Boot整合Apache BookKeeper教程 1. 簡介 Apache BookKeeper 是一個高性能、持久化的分布式日志存儲系統&#xff0c;適用于需要強一致性和高吞吐量的…

蘋果HFS+56TB存儲MOV文件出錯的恢復方法

HFS文件系統是Apple電腦中默認的最常見的文件系統。HFS來源于UNIX&#xff0c;優勢就是穩定性&#xff0c;另外HFS是支持日志功能的&#xff0c;所以很多存儲設備也采用了HFS文件系統。再穩定的文件系統也有“馬失前蹄”的時候&#xff0c;下面就來聊下HFS出現文件出錯、丟失時…

電源電路篇

電源電路篇 一、LDO-Low Dropout Regulator(低壓差線性穩壓器)1.1 AMS1117-3.3V芯片 二、DCDC-Direct Current to Direct Current(開關穩壓器)2.1 降壓(Buck)電路2.1.1 TPS5450-5V芯片 一、LDO-Low Dropout Regulator(低壓差線性穩壓器) LDO是一種線性穩壓器&#xff0c;用于提…

java項目之在線購物系統(源碼+文檔)

項目簡介 在線購物系統實現了以下功能&#xff1a; 使用在線購物系統的用戶分管理員和用戶兩個角色的權限子模塊。 管理員所能使用的功能主要有&#xff1a;主頁、個人中心、用戶管理、商品分類管理、商品信息管理、系統管理、訂單管理等。 用戶可以實現主頁、個人中心、我的…

go語言中空結構體

空結構體(struct{}) 普通理解 在結構體中&#xff0c;可以包裹一系列與對象相關的屬性&#xff0c;但若該對象沒有屬性呢&#xff1f;那它就是一個空結構體。 空結構體&#xff0c;和正常的結構體一樣&#xff0c;可以接收方法函數。 type Lamp struct{}func (l Lamp) On()…

Unity實現連連看連線效果

1.一個比較簡單的向量計算&#xff0c;用的LineRenderer實現&#xff1b; 已知起始A點和終點C點&#xff0c;求B點&#xff1b; 先計算A點到C點的向量取歸一化當做方向&#xff0c;再給定一個“模長”&#xff08;B點到A點的模長&#xff09;乘以該方向&#xff0c;最后加上L…

【MySQL】觸發器與存儲引擎

目錄 觸發器基本概念觸發器操作創建觸發器NEW 與 OLD查看觸發器刪除觸發器 注意事項 存儲引擎基本概念基本操作查詢當前數據庫支持的存儲引擎查看當前的默認存儲引擎查看某個表用的存儲引擎創建表時指定存儲引擎修改表的存儲引擎 觸發器 基本概念 概述&#xff1a; 觸發器&a…

能“嘎嘎提升”提升用戶居住體驗的智能家居物聯網框架推薦!

智能家居在日常生活中給我們的帶來了更多的便利&#xff0c;更讓有些用戶切實地體會到了科技的魅力&#xff0c;對于想要打造屬于自己的智能家居氛圍感的用戶們&#xff0c;以下是一些能夠幫助提升居住體驗的智能家居物聯網框架及應用&#xff1a; 1. 涂鴉智能&#xff08;Tuy…

DevEco Studio的使用

目錄 1.創建ArkTS工程 2.ArkTS工程目錄結構&#xff08;Stage模型&#xff09; 構建第一個頁面 構建第二個頁面 實現頁面間的跳轉 1.創建ArkTS工程 若首次打開DevEco Studio&#xff0c;請點擊Create Project創建工程。如果已經打開了一個工程&#xff0c;請在菜單欄選擇…

性能監控——vmstat

性能監控——vmstat ? 性能監控是對 IT 系統運行效率和有效性的系統觀察和測量。它涉及收集、分析和報告各種組件&#xff08;包括應用程序、網絡、服務器和數據庫&#xff09;的關鍵性能指標 (KPI)。此過程使用專門的工具來跟蹤響應時間、吞吐量、資源利用率和錯誤率等指標。…

搭建Python量化開發環境:從零開始的完整指南

搭建Python量化開發環境&#xff1a;從零開始的完整指南 在量化投資領域&#xff0c;一個穩定且高效的開發環境是成功的關鍵。本文將引導你一步步搭建起自己的Python量化開發環境&#xff0c;確保你能夠順利開始編寫和運行量化策略。 &#x1f680;量化軟件開通 &#x1f68…

圖像分割的mask有空洞怎么修補

分享一個對實例分割mask修補的方法&#xff0c;希望對大家有所幫助。 1. 這是我準備分割的圖片 2 分割結果 可以看到衣服部分有一些沒分割出來&#xff0c;二值化圖片能清晰看到衣服部分有些黑色未分出的地方。 3 補全mask區域 import cv2 import numpy as npdef fill_mask_h…

Qt 控件概述 QLabel

目錄 QLabel顯示類控件 label如何做到與窗口同步變化 邊框 Frame QLabel顯示類控件 ?? ?? textFormat &#xff1a;設置文件格式 ? Pixmap &#xff1a;標簽圖片 label如何做到與窗口同步變化 Qt中對應用戶的操作 &#xff1a; 事件和信號 拖拽窗口大小就會觸發…

詞頻統計 ccf-csp 2024-2-1

在學習了文本處理后&#xff0c;小 P 對英語書中的 n篇文章進行了初步整理。 具體來說&#xff0c;小 P將所有的英文單詞都轉化為了整數編號。 假設這 n 篇文章中共出現了 m個不同的單詞&#xff0c;則把它們從 1到 m進行編號。 這樣&#xff0c;每篇文章就簡化為了一個整數…

用爬蟲解鎖 Shopee 店鋪商品數據,開啟電商新洞察

在電商競爭白熱化的當下&#xff0c;Shopee 作為全球知名的電商平臺&#xff0c;匯聚了海量的商品與商機。對于電商從業者、數據分析師、創業者來說&#xff0c;精準掌握 Shopee 店鋪的商品信息&#xff0c;就如同手握一把開啟財富大門的鑰匙。而爬蟲技術&#xff0c;無疑是幫助…