Chuỗi bài: C++
cpp
422 dòng
· Cập nhật 2026-04-03
exceptions.cpp
C++/Part4_現代CPP/Ch18_例外處理/exceptions.cpp
// ============================================================
// Ch18 — 例外處理基礎(Exception Handling Basics)
// 編譯:g++ -std=c++17 -Wall -o exceptions exceptions.cpp
// ============================================================
#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
#include <fstream>
#include <sstream>
// ── 輔助函式 ──
void section(const std::string& title) {
std::cout << "\n========================================\n";
std::cout << " " << title << "\n";
std::cout << "========================================\n";
}
// ── 堆疊回溯示範用類別 ──
class TrackedResource {
public:
explicit TrackedResource(const std::string& name) : name_(name) {
std::cout << " TrackedResource(\"" << name_ << "\") 建構\n";
}
~TrackedResource() {
std::cout << " TrackedResource(\"" << name_ << "\") 解構(堆疊回溯)\n";
}
private:
std::string name_;
};
// ── 層層呼叫的函式(展示堆疊回溯)──
void level3() {
TrackedResource r3("Level-3 資源");
std::cout << " level3:即將拋出例外...\n";
throw std::runtime_error("在 level3 發生錯誤");
}
void level2() {
TrackedResource r2("Level-2 資源");
std::cout << " level2:呼叫 level3...\n";
level3();
std::cout << " level2:這行不會被執行\n";
}
void level1() {
TrackedResource r1("Level-1 資源");
std::cout << " level1:呼叫 level2...\n";
level2();
std::cout << " level1:這行不會被執行\n";
}
// ── 安全除法 ──
double safeDivide(double numerator, double denominator) {
if (denominator == 0.0) {
throw std::invalid_argument("除數不能為零");
}
return numerator / denominator;
}
// ── 陣列存取 ──
int safeAccess(const std::vector<int>& vec, int index) {
if (index < 0 || static_cast<size_t>(index) >= vec.size()) {
throw std::out_of_range(
"索引 " + std::to_string(index) +
" 超出範圍 [0, " + std::to_string(vec.size() - 1) + "]"
);
}
return vec[static_cast<size_t>(index)];
}
// ── 解析整數 ──
int parseInteger(const std::string& str) {
try {
size_t pos = 0;
int result = std::stoi(str, &pos);
if (pos != str.length()) {
throw std::invalid_argument("字串包含非數字字元:\"" + str + "\"");
}
return result;
}
catch (const std::out_of_range&) {
throw std::out_of_range("數值超出 int 範圍:\"" + str + "\"");
}
catch (const std::invalid_argument&) {
throw std::invalid_argument("無法解析為整數:\"" + str + "\"");
}
}
// ── 模擬檔案操作 ──
std::string readFile(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
throw std::runtime_error("無法開啟檔案:" + filename);
}
std::stringstream buffer;
buffer << file.rdbuf();
return buffer.str();
}
int main() {
std::cout << "===== Ch18:例外處理基礎示範 =====\n";
// ──────────────────────────────────────────
// 1. 基本 try / catch / throw
// ──────────────────────────────────────────
section("1. 基本 try / catch / throw");
{
// 拋出與捕捉 int
try {
std::cout << "嘗試拋出 int...\n";
throw 42;
}
catch (int e) {
std::cout << "捕捉到 int: " << e << "\n";
}
// 拋出與捕捉 const char*
try {
std::cout << "\n嘗試拋出 const char*...\n";
throw "Something went wrong";
}
catch (const char* msg) {
std::cout << "捕捉到字串: " << msg << "\n";
}
// 拋出與捕捉 std::string
try {
std::cout << "\n嘗試拋出 std::string...\n";
throw std::string("String exception");
}
catch (const std::string& msg) {
std::cout << "捕捉到 std::string: " << msg << "\n";
}
}
// ──────────────────────────────────────────
// 2. 標準例外
// ──────────────────────────────────────────
section("2. 標準例外");
{
// runtime_error
try {
throw std::runtime_error("執行時錯誤示範");
}
catch (const std::runtime_error& e) {
std::cout << "runtime_error: " << e.what() << "\n";
}
// invalid_argument
try {
throw std::invalid_argument("引數無效");
}
catch (const std::invalid_argument& e) {
std::cout << "invalid_argument: " << e.what() << "\n";
}
// out_of_range(來自 std::vector::at)
try {
std::vector<int> v = {1, 2, 3};
std::cout << "\n存取 v.at(10)...\n";
std::cout << v.at(10) << "\n"; // 會拋出 out_of_range
}
catch (const std::out_of_range& e) {
std::cout << "out_of_range: " << e.what() << "\n";
}
// overflow_error
try {
throw std::overflow_error("數值溢位");
}
catch (const std::overflow_error& e) {
std::cout << "overflow_error: " << e.what() << "\n";
}
}
// ──────────────────────────────────────────
// 3. 以參考捕捉(Catch by Reference)
// ──────────────────────────────────────────
section("3. 以參考捕捉的重要性");
{
// 正確:以 const 參考捕捉
try {
throw std::runtime_error("衍生類別的錯誤");
}
catch (const std::exception& e) {
// 即使捕捉基底類別,what() 仍回傳正確訊息
std::cout << "以 const& 捕捉: " << e.what() << "\n";
}
std::cout << "\n以值捕捉會導致物件切割(slicing):\n";
std::cout << R"(
// 不好的做法:
catch (std::exception e) {
// 如果拋出的是 runtime_error,
// 會被切割成 exception,遺失額外資訊
}
)";
std::cout << "→ 永遠使用 const& 捕捉例外\n";
}
// ──────────────────────────────────────────
// 4. 多個 catch 區塊
// ──────────────────────────────────────────
section("4. 多個 catch 區塊(順序很重要)");
{
auto testException = [](int type) {
try {
switch (type) {
case 1: throw std::out_of_range("越界");
case 2: throw std::invalid_argument("無效引數");
case 3: throw std::runtime_error("執行時錯誤");
case 4: throw 999;
default: break;
}
}
// 衍生類別在前
catch (const std::out_of_range& e) {
std::cout << " [out_of_range] " << e.what() << "\n";
}
catch (const std::invalid_argument& e) {
std::cout << " [invalid_argument] " << e.what() << "\n";
}
// 基底類別在後
catch (const std::exception& e) {
std::cout << " [exception] " << e.what() << "\n";
}
// 萬用捕捉放最後
catch (...) {
std::cout << " [catch-all] 未知例外\n";
}
};
std::cout << "type=1: "; testException(1);
std::cout << "type=2: "; testException(2);
std::cout << "type=3: "; testException(3);
std::cout << "type=4: "; testException(4);
}
// ──────────────────────────────────────────
// 5. catch-all(...)
// ──────────────────────────────────────────
section("5. catch-all (...)");
{
try {
throw 3.14; // 拋出 double
}
catch (const std::exception& e) {
std::cout << "標準例外: " << e.what() << "\n";
}
catch (...) {
std::cout << "catch(...) 捕捉到未知型別的例外\n";
std::cout << "(無法取得例外資訊)\n";
}
}
// ──────────────────────────────────────────
// 6. 堆疊回溯(Stack Unwinding)
// ──────────────────────────────────────────
section("6. 堆疊回溯");
{
std::cout << "呼叫 level1 → level2 → level3,在 level3 拋出例外:\n\n";
try {
level1();
}
catch (const std::runtime_error& e) {
std::cout << "\n捕捉到例外:" << e.what() << "\n";
std::cout << "所有資源已透過解構子自動釋放 ✓\n";
}
}
// ──────────────────────────────────────────
// 7. 巢狀 try/catch 與重新拋出
// ──────────────────────────────────────────
section("7. 巢狀 try/catch 與重新拋出");
{
try {
try {
throw std::runtime_error("原始錯誤");
}
catch (const std::runtime_error& e) {
std::cout << "內層捕捉:" << e.what() << "\n";
std::cout << "記錄錯誤後重新拋出...\n";
throw; // 重新拋出(保留原始型別)
}
}
catch (const std::exception& e) {
std::cout << "外層捕捉:" << e.what() << "\n";
}
std::cout << "\n注意 throw vs throw e 的差異:\n";
std::cout << " throw; → 重新拋出原始例外(保留型別)\n";
std::cout << " throw e; → 拋出 e 的副本(可能切割)\n";
}
// ──────────────────────────────────────────
// 8. 實際應用:安全除法
// ──────────────────────────────────────────
section("8. 實際應用:安全除法");
{
double pairs[][2] = {{10, 3}, {100, 0}, {-7, 2}, {0, 5}};
for (auto& pair : pairs) {
try {
double result = safeDivide(pair[0], pair[1]);
std::cout << pair[0] << " / " << pair[1]
<< " = " << result << "\n";
}
catch (const std::invalid_argument& e) {
std::cout << pair[0] << " / " << pair[1]
<< " → 錯誤:" << e.what() << "\n";
}
}
}
// ──────────────────────────────────────────
// 9. 實際應用:安全陣列存取
// ──────────────────────────────────────────
section("9. 實際應用:安全陣列存取");
{
std::vector<int> data = {10, 20, 30, 40, 50};
int indices[] = {0, 2, 4, 5, -1};
for (int idx : indices) {
try {
int val = safeAccess(data, idx);
std::cout << "data[" << idx << "] = " << val << "\n";
}
catch (const std::out_of_range& e) {
std::cout << "data[" << idx << "] → " << e.what() << "\n";
}
}
}
// ──────────────────────────────────────────
// 10. 實際應用:字串轉整數
// ──────────────────────────────────────────
section("10. 實際應用:字串解析");
{
std::string testCases[] = {
"42", "-7", "3.14", "abc", "999999999999999999", "123abc"
};
for (const auto& s : testCases) {
try {
int val = parseInteger(s);
std::cout << "\"" << s << "\" → " << val << "\n";
}
catch (const std::invalid_argument& e) {
std::cout << "\"" << s << "\" → [invalid_argument] "
<< e.what() << "\n";
}
catch (const std::out_of_range& e) {
std::cout << "\"" << s << "\" → [out_of_range] "
<< e.what() << "\n";
}
}
}
// ──────────────────────────────────────────
// 11. 實際應用:檔案操作
// ──────────────────────────────────────────
section("11. 實際應用:檔案操作");
{
try {
std::string content = readFile("不存在的檔案.txt");
std::cout << "檔案內容:" << content << "\n";
}
catch (const std::runtime_error& e) {
std::cout << "錯誤:" << e.what() << "\n";
}
}
// ──────────────────────────────────────────
// 12. 例外與 RAII
// ──────────────────────────────────────────
section("12. 例外與 RAII 的完美搭配");
{
std::cout << "RAII 確保即使在例外路徑上,資源也能正確釋放:\n\n";
try {
std::string s = "會被自動釋放的字串";
std::vector<int> v = {1, 2, 3, 4, 5};
// std::unique_ptr<Resource> r = std::make_unique<Resource>(...);
// std::lock_guard<std::mutex> lock(mtx);
// std::fstream file("data.txt");
std::cout << " s = \"" << s << "\"\n";
std::cout << " v.size() = " << v.size() << "\n";
throw std::runtime_error("模擬錯誤");
// 以下不會執行,但 s, v 等物件的解構子會被呼叫
}
catch (const std::exception& e) {
std::cout << "\n捕捉到例外:" << e.what() << "\n";
std::cout << "所有 RAII 物件(string, vector 等)已自動清理 ✓\n";
}
}
// ──────────────────────────────────────────
// 13. 例外 vs 錯誤碼
// ──────────────────────────────────────────
section("13. 例外 vs 錯誤碼比較");
{
std::cout << "┌──────────────┬─────────────┬─────────────┐\n";
std::cout << "│ 比較項目 │ 例外 │ 錯誤碼 │\n";
std::cout << "├──────────────┼─────────────┼─────────────┤\n";
std::cout << "│ 可忽略性 │ 不可忽略 │ 容易被忽略 │\n";
std::cout << "│ 程式碼清晰度 │ 高 │ 低(多 if) │\n";
std::cout << "│ 正常路徑效能 │ 零/極低開銷 │ 每次都檢查 │\n";
std::cout << "│ 錯誤路徑效能 │ 較慢 │ 快 │\n";
std::cout << "│ 建構子錯誤 │ 唯一選擇 │ 無法使用 │\n";
std::cout << "│ 適用場景 │ 罕見錯誤 │ 可預期結果 │\n";
std::cout << "└──────────────┴─────────────┴─────────────┘\n";
}
std::cout << "\n===== 例外處理基礎示範結束 =====\n";
return 0;
}
Bài viết liên quan
C++
c
Cập nhật 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/deviceAlpha.h).
Đọc bài viết →
C++
c
Cập nhật 2026-07-21
finalproject.c
finalproject.c — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.c).
Đọc bài viết →
C++
cpp
Cập nhật 2026-07-21
finalproject.cpp
finalproject.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.cpp).
Đọc bài viết →
C++
c
Cập nhật 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/deviceAlpha.h).
Đọc bài viết →
C++
c
Cập nhật 2026-07-21
lab8.c
lab8.c — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.c).
Đọc bài viết →
C++
cpp
Cập nhật 2026-07-21
lab8.cpp
lab8.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.cpp).
Đọc bài viết →