[學習]淺談C++異常處理(代碼示例)

淺談C++異常處理

文章目錄

    • 淺談C++異常處理
      • 一、異常處理基礎
        • 1.異常的概念與作用
        • 2.C++異常處理機制(`try`、`catch`、`throw`)
        • 3.基本語法示例
      • 二、標準異常類
        • 1.常見標準異常類:
        • 2.自定義異常類的實現
      • 三、異常安全與最佳實踐
        • 1. RAII(資源獲取即初始化)與異常安全
        • 2. `noexcept` 關鍵字的作用與使用場景
        • 3. 避免異常濫用與性能影響
      • 四、高級異常處理技術
        • 1. 嵌套異常(`std::nested_exception`)
        • 2. 異常傳播與重新拋出(`throw;`)
        • 3. 異常處理在多線程環境中的注意事項
      • 五、實際應用案例
        • 1. 文件操作中的異常處理
        • 2. 網絡編程中的異常管理
        • 3. 大型項目中的異常策略設計


一、異常處理基礎

1.異常的概念與作用

異常是程序在運行時發生的意外或錯誤情況(如除零錯誤、內存不足、文件不存在等),它打破了正常的程序執行流程。異常處理機制允許開發者:

  1. 分離錯誤處理代碼:將正常邏輯與錯誤處理解耦,提升代碼可讀性
  2. 跨函數傳遞錯誤:異常可沿調用棧向上傳遞,無需每層函數顯式檢查錯誤
  3. 標準化錯誤報告:通過異常類型和消息規范錯誤信息
2.C++異常處理機制(trycatchthrow
  1. throw:當檢測到錯誤時,用throw拋出異常對象(如內置類型、自定義類或標準庫異常)
    • 示例:throw 42;(拋出整型)或 throw std::out_of_range("Index invalid");
  2. try:包裹可能引發異常的代碼塊
  3. catch:捕獲并處理特定類型的異常,支持多級捕獲(類似switch-case的匹配機制)
3.基本語法示例
#include <iostream>
#include <stdexcept> // 包含標準異常類double divide(int a, int b) {if (b == 0) {throw std::runtime_error("Division by zero"); // 拋出運行時錯誤}return static_cast<double>(a) / b;
}int main() {try {std::cout << divide(10, 2) << std::endl;  // 正常執行std::cout << divide(5, 0) << std::endl;   // 觸發異常} catch (const std::runtime_error& e) {       // 捕獲特定異常std::cerr << "[ERROR] " << e.what() << std::endl;} catch (...) {                               // 捕獲所有未處理的異常std::cerr << "Unknown exception occurred" << std::endl;}return 0;
}

輸出示例

5
[ERROR] Division by zero

應用場景

  1. 文件操作失敗處理

    • 當使用std::ifstream打開或讀取文件時,若文件不存在、權限不足或格式錯誤,會拋出std::ifstream::failure異常。例如:
      try {std::ifstream file("data.txt");if (!file) throw std::ifstream::failure("Failed to open file");// 文件讀取操作...
      } catch (const std::ifstream::failure& e) {std::cerr << "File error: " << e.what() << std::endl;
      }
      
    • 典型場景:配置文件加載、日志文件讀取或數據導入時驗證文件有效性。
  2. 動態內存分配失敗處理

    • 使用new分配大量內存時,若系統內存不足會拋出std::bad_alloc。例如:
      try {int* large_array = new int[1000000000]; // 可能分配失敗
      } catch (const std::bad_alloc& e) {std::cerr << "Memory allocation failed: " << e.what() << std::endl;
      }
      
    • 優化建議:在嵌入式系統或高性能計算中,可預先檢查可用內存或使用std::nothrow替代方案。
  3. 自定義異常處理業務邏輯

    • 定義繼承自std::exception的異常類(如InvalidUserInputException),用于驗證用戶輸入。例如:
      class InvalidUserInputException : public std::exception {
      public:const char* what() const noexcept override {return "User input contains invalid characters";}
      };void validateInput(const std::string& input) {if (input.find('@') == std::string::npos) {throw InvalidUserInputException();}
      }
      
    • 擴展場景:電商系統中校驗訂單金額非負,或游戲開發中檢查玩家非法操作。

最佳實踐

  • 優先捕獲具體異常類型(如std::bad_alloc而非通用std::exception)。
  • 在異常處理中記錄上下文信息(如文件名、輸入值)以便調試。
  • 資源管理類(如數據庫連接)應在析構函數中釋放資源,而非依賴異常處理。

二、標準異常類

C++標準庫提供了豐富的異常類體系,主要通過std::exception基類及其派生類來實現異常處理機制。這些異常類通常包含在<exception>頭文件中,為開發者提供了標準化的異常處理方式。

1.常見標準異常類:
  1. std::exception
    所有標準異常類的基類,提供what()虛函數用于獲取異常描述信息。

  2. std::runtime_error

    • 用于表示程序運行時發生的錯誤(如文件不存在、網絡連接失敗等)
    • 典型派生類:
      • std::overflow_error - 算術運算溢出
      • std::underflow_error - 算術運算下溢
      • std::range_error - 超出有效范圍
  3. std::logic_error

    • 用于表示程序邏輯錯誤(如無效參數、違反前置條件等)
    • 典型派生類:
      • std::invalid_argument - 無效參數
      • std::out_of_range - 超出允許范圍
      • std::length_error - 超出最大允許長度
2.自定義異常類的實現

在實際開發中,我們經常需要創建特定于應用的異常類。通過繼承std::exception或其派生類,可以保持與標準異常處理機制的一致性。

#include <exception>
#include <string>class MyCustomException : public std::exception {
private:std::string error_msg;  // 存儲詳細的錯誤信息public:// 構造函數,允許傳入自定義錯誤信息explicit MyCustomException(const std::string& msg) : error_msg(msg) {}// 重寫what()方法,返回錯誤描述const char* what() const noexcept override {return error_msg.c_str();}// 示例用法:// throw MyCustomException("File processing failed: invalid format");
};

自定義異常類的典型應用場景:

  1. 文件處理異常(如文件格式不符、權限不足)
  2. 網絡通信異常(如連接超時、協議錯誤)
  3. 業務邏輯異常(如交易金額超限、用戶狀態異常)

實現建議:

  1. 繼承自std::exception或其標準派生類
  2. 提供詳細的錯誤信息存儲機制
  3. 確保what()方法不會拋出異常(使用noexcept
  4. 考慮添加額外的錯誤代碼或上下文信息字段

三、異常安全與最佳實踐

1. RAII(資源獲取即初始化)與異常安全

RAII(Resource Acquisition Is Initialization)是一種利用對象生命周期管理資源的核心編程范式。通過將資源的獲取與對象的構造綁定,資源的釋放與對象的析構綁定,確保即使在異常發生時資源也能被正確釋放,避免內存泄漏或資源泄露。

典型應用場景

  • 文件操作:使用 std::ifstreamstd::ofstream 封裝文件句柄,析構時自動關閉文件。
  • 動態內存管理:通過 std::unique_ptrstd::shared_ptr 管理堆內存,無需手動 delete
  • 鎖管理:利用 std::lock_guard 在作用域內自動加鎖和解鎖,避免死鎖。

示例代碼

void processFile(const std::string& filename) {std::ifstream file(filename); // 構造函數打開文件if (!file.is_open()) {throw std::runtime_error("Failed to open file");}// 文件操作...(若此處拋出異常,file 析構時會自動關閉文件)
} // 作用域結束,file 析構自動關閉文件
2. noexcept 關鍵字的作用與使用場景

noexcept 是 C++11 引入的關鍵字,用于顯式聲明函數不會拋出異常。合理使用可以優化性能,并為編譯器提供更多優化機會。

主要作用

  • 性能優化:標記為 noexcept 的函數可能觸發移動語義而非拷貝(如 std::vector 的重新分配)。
  • 契約聲明:明確告知調用者該函數不會拋出異常,簡化錯誤處理邏輯。

適用場景

  • 析構函數、移動構造函數、移動賦值運算符等必須聲明為 noexcept(標準庫容器依賴此保證)。
  • 簡單工具函數(如數學計算)、資源釋放邏輯等無異常風險的函數。

示例

class Buffer {
public:Buffer(Buffer&& other) noexcept // 移動構造函數不拋出異常: data_(other.data_), size_(other.size_) {other.data_ = nullptr;}~Buffer() noexcept { delete[] data_; } // 析構函數通常不拋出異常
private:int* data_;size_t size_;
};
3. 避免異常濫用與性能影響

異常機制雖然強大,但濫用可能導致性能下降或代碼可維護性降低。需注意以下原則:

最佳實踐

  • 避免頻繁拋出異常:異常處理成本較高,高頻場景(如循環內)建議改用錯誤碼或狀態檢查。
  • 異常 vs 錯誤碼
    • 使用異常處理不可恢復的錯誤(如內存不足、文件損壞)。
    • 使用錯誤碼處理預期內的錯誤(如用戶輸入無效)。
  • 禁用異常的場景:嵌入式系統等對運行時開銷敏感的環境,可通過編譯選項(如 -fno-exceptions)禁用異常。

性能對比示例

// 低效:在熱路徑中拋出異常
for (int i = 0; i < 10000; ++i) {try {riskyOperation();} catch (...) { /* 處理 */ }
}// 高效:提前檢查避免異常
for (int i = 0; i < 10000; ++i) {if (canPerformOperation()) { // 預先驗證safeOperation();}
}

總結:異常安全的核心是資源管理確定性異常使用克制性,結合 RAII 與 noexcept 可顯著提升代碼健壯性。


四、高級異常處理技術

1. 嵌套異常(std::nested_exception

嵌套異常允許異常對象包含另一個異常,形成異常鏈,便于捕獲和調試多層異常。這在復雜系統中特別有用,例如框架或庫的調用棧中可能拋出多種異常的場景。

示例代碼

#include <exception>
#include <iostream>
#include <stdexcept>void handle_nested_exception(const std::exception& e) {try {std::rethrow_if_nested(e); // 嘗試重新拋出嵌套異常  } catch (const std::exception& nested) {std::cerr << "Nested Exception: " << nested.what() << std::endl;handle_nested_exception(nested); // 遞歸處理嵌套異常  }
}int main() {try {try {throw std::runtime_error("Primary error");} catch (...) {std::throw_with_nested(std::logic_error("Wrapper error"));}} catch (const std::exception& e) {std::cerr << "Outer Exception: " << e.what() << std::endl;handle_nested_exception(e);}return 0;
}

應用場景

  • 在多層調用的框架中,捕獲底層異常并附加高層上下文信息。
  • 日志記錄時保留完整的異常鏈,便于問題溯源。

2. 異常傳播與重新拋出(throw;

在異常處理塊中,使用 throw; 可以重新拋出當前捕獲的異常,保持原始異常類型和調用棧信息。

關鍵點

  • 僅當在 catch 塊內使用時有效,否則行為未定義。
  • 適用于需要部分處理異常但最終仍由上級調用者處理的場景。

示例代碼

void process_data() {try {// 可能拋出異常的代碼  throw std::runtime_error("Data processing failed");} catch (...) {std::cerr << "Partial handling in process_data()" << std::endl;throw; // 重新拋出  }
}int main() {try {process_data();} catch (const std::runtime_error& e) {std::cerr << "Handled in main(): " << e.what() << std::endl;}return 0;
}

典型場景

  • 中間層函數需要清理資源(如關閉文件),但異常仍需傳遞給調用者。
  • 實現異常過濾器,選擇性重新拋出特定異常類型。

3. 異常處理在多線程環境中的注意事項

多線程中未捕獲的異常會導致程序終止(C++11 后通過 std::terminate),需顯式處理線程內異常。

解決方案

  1. 使用 try-catch 包裹線程入口函數
    void thread_worker() {try {// 線程邏輯  } catch (const std::exception& e) {std::cerr << "Thread error: " << e.what() << std::endl;}
    }
    
  2. 傳遞異常到主線程(通過 std::promise 或全局變量):
    std::promise<void> promise;
    std::thread t([&promise] {try {// 線程邏輯  promise.set_value();} catch (...) {promise.set_exception(std::current_exception());}
    });
    try {promise.get_future().get(); // 主線程捕獲異常  
    } catch (const std::exception& e) {std::cerr << "Main thread caught: " << e.what() << std::endl;
    }
    t.join();
    

注意事項

  • 避免跨線程直接 throw,因棧展開可能不同步。
  • 使用 std::exception_ptr 存儲和跨線程傳遞異常對象。

五、實際應用案例

1. 文件操作中的異常處理

文件操作是編程中常見的場景,但可能會遇到文件不存在、權限不足或磁盤空間不足等問題。合理的異常處理可以避免程序崩潰,并提供友好的錯誤提示。

示例代碼:讀取文件內容并處理異常

def read_file(file_path):try:with open(file_path, 'r', encoding='utf-8') as file:content = file.read()print(f"文件內容讀取成功:\n{content}")except FileNotFoundError:print(f"錯誤:文件 '{file_path}' 不存在,請檢查路徑。")except PermissionError:print(f"錯誤:沒有權限訪問文件 '{file_path}'。")except UnicodeDecodeError:print(f"錯誤:文件 '{file_path}' 編碼格式不支持,請指定正確的編碼。")except Exception as e:print(f"未知錯誤:{str(e)}")# 調用示例
read_file("example.txt")

應用場景

  • 讀取配置文件時,避免因文件缺失導致程序中斷。
  • 批量處理文件時,記錄錯誤文件路徑并跳過,不影響后續文件操作。

2. 網絡編程中的異常管理

網絡請求可能因連接超時、服務器錯誤或數據格式問題而失敗。異常管理能提高程序的健壯性,尤其是在高并發或分布式系統中。

示例代碼:HTTP請求異常處理

import requestsdef fetch_data(url):try:response = requests.get(url, timeout=5)response.raise_for_status()  # 檢查HTTP狀態碼return response.json()except requests.exceptions.Timeout:print(f"請求超時:服務器未在5秒內響應(URL: {url})。")except requests.exceptions.HTTPError as e:print(f"HTTP錯誤:狀態碼 {e.response.status_code}(URL: {url})。")except requests.exceptions.JSONDecodeError:print(f"數據格式錯誤:返回內容非JSON格式(URL: {url})。")except Exception as e:print(f"網絡請求失敗:{str(e)}")# 調用示例
data = fetch_data("https://api.example.com/data")

應用場景

  • API調用時,處理服務器返回的錯誤狀態碼(如404、500)。
  • 爬蟲程序中跳過失效鏈接,避免因單次請求失敗終止任務。

3. 大型項目中的異常策略設計

在大型項目中,異常處理需要統一策略,包括日志記錄、錯誤分類和恢復機制。常見的做法包括自定義異常類和全局異常攔截。

示例代碼:自定義異常與全局處理

# 自定義異常類
class DatabaseConnectionError(Exception):"""數據庫連接失敗時拋出"""passclass InvalidInputError(Exception):"""用戶輸入非法時拋出"""pass# 全局異常處理器
def handle_exceptions(func):def wrapper(*args, **kwargs):try:return func(*args, **kwargs)except DatabaseConnectionError as e:log_error(f"數據庫錯誤:{str(e)}")show_user_message("系統繁忙,請稍后重試。")except InvalidInputError as e:log_error(f"輸入錯誤:{str(e)}")show_user_message("請輸入有效數據。")except Exception as e:log_error(f"未捕獲的異常:{str(e)}")show_user_message("系統發生未知錯誤。")return wrapper# 使用裝飾器管理異常
@handle_exceptions
def process_user_data(user_input):if not user_input.isdigit():raise InvalidInputError("輸入必須為數字")# 模擬數據庫操作if not connect_database():raise DatabaseConnectionError("無法連接MySQL")# 輔助函數
def log_error(message):with open("error.log", "a") as f:f.write(f"[ERROR] {message}\n")def show_user_message(message):print(message)

應用場景

  • 微服務架構中,統一處理跨服務調用的超時或數據格式異常。
  • Web框架(如Django、Flask)中通過中間件攔截全局異常,返回標準化錯誤頁面或JSON響應。

關鍵策略

  1. 分層處理:底層捕獲技術異常(如IO錯誤),業務層捕獲邏輯異常(如無效訂單)。
  2. 日志分級:錯誤(Error)記錄異常堆棧,警告(Warning)記錄可恢復問題。
  3. 用戶反饋:對客戶端隱藏技術細節,提供明確的操作指引。

研究學習不易,點贊易。
工作生活不易,收藏易,點收藏不迷茫 :)


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

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

相關文章

PHP學習筆記(十)

extends 一個類可以在聲明中用extends關鍵字繼承另一個類的方法和屬性。PHP不支持多重繼承&#xff0c;一個類只能繼承一個基類。 被繼承的方法和屬性可以通過同樣的名字重新聲明被覆蓋&#xff0c;但是如果父類定義或者常量時是使用類final&#xff0c;則不可被覆蓋&#xff…

rt-linux里的泛rtmutex鎖的調用鏈整體分析

一、背景 linux系統里有非常多的鎖種類&#xff0c;除了spinlock&#xff0c;mutex&#xff0c;rwlock&#xff0c;rwsem&#xff0c;還有rcu及順序鎖&#xff0c;這里面還有不少鎖變種&#xff0c;比如spinlock的帶bh或者irq字樣的lock/unlock&#xff0c;還有nmi里可以用的順…

LLM多平臺統一調用系統-LiteLLM概述

概述 在當今快速發展的AI領域&#xff0c;大語言模型(LLM)已成為技術創新的核心驅動力。然而&#xff0c;隨著市場上涌現出越來越多的LLM提供商&#xff08;如OpenAI、Anthropic、Google Gemini、AWS Bedrock等&#xff09;&#xff0c;開發者面臨著一個日益復雜的問題&#x…

C#實現MCP Client 與 LLM 連接,抓取網頁內容功能!

該專欄優先在飛書發布&#xff0c;歡迎收藏關注&#xff01; https://www.feishu.cn/community/article?id7507084665509904403 前面的課程&#xff0c;我們已經用C#實現了&#xff0c;自己的MCP Client。 下面我們一起來實現&#xff0c;MCP Client與LLM 對接。 一、添加依…

并發編程(6)

指令重排序 指令重排序是指在程序執行過程中&#xff0c;為了提高性能&#xff0c;編譯器或處理器會對指令的執行順序進行重新排列。 指令重排序導致可見性消失 在多線程環境下&#xff0c;每個線程都有自己的工作內存&#xff0c;線程對變量的操作是在工作內存中進行的&…

鴻蒙倉頡開發語言實戰教程:頁面跳轉和傳參

前兩天分別實現了商城應用的首頁和商品詳情頁面&#xff0c;今天要分享新的內容&#xff0c;就是這兩個頁面之間的相互跳轉和傳遞參數。 首先我們需要兩個頁面。如果你的項目中還沒有第二個頁面&#xff0c;可以右鍵cangjie文件夾新建倉頡文件&#xff1a; 新建的文件里面沒什…

Java 學習筆記:注解、泛型與 IO 流

目錄 課程目標 Java 注解(Annotation) 1. 概念與作用 2. 自定義注解示例 3. JDK 內置注解 4.注釋 Java 泛型(Generics) 1. 基本語法 2. 通配符與上下限 3. 常見應用場景 Java IO 流 1. 流的分類1.File文件類 2. 字節流與字符流 3. 經典示例:文件拷貝 總結與…

git倉庫代碼操作

1、從gitee下載代碼提交到本地github倉庫&#xff0c;保留提交記錄 # 查看當前分支 git branch# 查看當前遠程倉庫 git remote -v# 確保所有更改已提交 git add . git commit -m "準備提交到GitLab"# 添加GitLab遠程倉庫 git remote add gitlab https://gitlab.com/…

Thinkphp6使用token+Validate驗證防止表單重復提交

htm頁面加 <input type"hidden" name"__token__" value"{:token()}" /> Validate 官方文檔 ThinkPHP官方手冊

Mcu_Bsdiff_Upgrade

系統架構 概述 MCU BSDiff 升級系統通過使用二進制差分技術&#xff0c;提供了一種在資源受限的微控制器上進行高效固件更新的機制。系統不傳輸和存儲完整的固件映像&#xff0c;而是只處理固件版本之間的差異&#xff0c;從而顯著縮小更新包并降低帶寬要求。 該架構遵循一個…

Spring Boot微服務架構(四):微服務的劃分原則

微服務劃分原則&#xff08;CRM系統案例說明&#xff09; 一、微服務劃分的核心原則 單一職責原則&#xff08;SRP&#xff09; 每個微服務只負責一個明確的業務功能服務邊界清晰&#xff0c;避免功能混雜便于獨立開發、測試和部署 業務領域驅動設計&#xff08;DDD&#xff0…

基于CNN卷積神經網絡的帶頻偏QPSK調制信號檢測識別算法matlab仿真

目錄 1.算法運行效果圖預覽 2.算法運行軟件版本 3.部分核心程序 4.算法理論概述 5.算法完整程序工程 1.算法運行效果圖預覽 (完整程序運行后無水印) 2.算法運行軟件版本 matlab2024b 3.部分核心程序 &#xff08;完整版代碼包含詳細中文注釋和操作步驟視頻&#xff09…

從機械應答到深度交互,移遠通信如何讓機器人“靈魂覺醒”?

你是否還在因機器人的“答非所問”而無奈&#xff0c;為它們的“反應慢半拍”而抓狂&#xff1f;別慌&#xff01;一場引領機器人實現“靈魂覺醒”的技術革命&#xff0c;正如同暗夜中悄然綻放的繁星&#xff0c;徹底顛覆人們對機器人的傳統認知。 5月20日&#xff0c;移遠通信…

軟件的技術架構、應用架構、業務架構、數據架構、部署架構

一、各架構定義 1. 技術架構&#xff08;Technical Architecture&#xff09; 定義&#xff1a;技術架構關注的是支撐系統運行的底層技術基礎設施和軟件平臺&#xff0c;包括硬件、操作系統、中間件、編程語言、框架、數據庫管理系統等技術組件的選擇和組合方式。它描述了系統…

HTML-前端

目錄 開始學習HTML 什么是 HTML? 剖析一個 HTML 元素 嵌套元素 塊級元素和內聯元素 空元素 屬性 為元素添加屬性 布爾屬性 省略包圍屬性值的引號 使用單引號還是雙引號&#xff1f; 剖析 HTML 文檔 HTML 中的空白 實體引用&#xff1a;在 HTML 中包含特殊字符 HT…

多態的總結

什么是多態&#xff1f; 答&#xff1a;多態是多種形態&#xff0c;是為了完成某種行為時&#xff0c;不同對象會產生不同的形態&#xff08;結合車票例子解釋&#xff09; 2. 什么是重載、重寫(覆蓋)、重定義(隱藏)&#xff1f; 答&#xff1a;重載的條件是&#xff1a;在同一…

VBA 讀取指定范圍內的單元格數據,生成csv文件

目錄 一. 需求二. 宏代碼三. 添加按鈕 一. 需求 ?有如下表格&#xff0c;現在想在Excel中添加一個按鈕 點擊按鈕之后&#xff0c;讀取該表格中的數據&#xff0c;生成csv文件將csv文件輸出到和Excel同級目錄 二. 宏代碼 Application.PathSeparator&#xff1a;路徑分隔符Cr…

【Code Agent Benchmark】論文分享No.15:TAU-Bench

論文名稱&#xff1a;τ-bench: A Benchmark for Tool-Agent-User Interaction in Real-World Domains 論文&#xff1a;https://arxiv.org/abs/2406.12045 機構&#xff1a;Sierra Github 鏈接&#xff1a;https://github.com/sierra-research/tau-bench# 簡介 相比于Swe-ben…

Linux下 使用 SSH 完成 Git 綁定 GitHub

文章目錄 1、檢查 SSH2、生成 SSH key3、添加 SSH key4、驗證綁定是否成功 1、檢查 SSH Git Bash 中輸入ssh命令&#xff0c;查看本機是否安裝 SSH&#xff1a; 2、生成 SSH key &#xff08;1&#xff09;輸入 ssh-keygen -t rsa 命令&#xff0c;表示我們指定 RSA 算法生…

Java 8 Stream 流操作全解析

文章目錄 **一、Stream 流簡介****二、Stream 流核心操作****1. 創建 Stream****2. 中間操作&#xff08;Intermediate Operations&#xff09;****filter(Predicate<T>)&#xff1a;過濾數據****1. 簡單條件過濾****2. 多條件組合****3. 過濾對象集合****4. 過濾 null 值…