Chuỗi bài: C++
cpp
602 dòng
· Cập nhật 2026-04-03
custom_exceptions.cpp
C++/Part4_現代CPP/Ch18_例外處理/custom_exceptions.cpp
// ============================================================
// Ch18 — 自訂例外類別(Custom Exception Classes)
// 編譯:g++ -std=c++17 -Wall -o custom_exceptions custom_exceptions.cpp
// ============================================================
#include <iostream>
#include <string>
#include <stdexcept>
#include <vector>
#include <sstream>
#include <cmath>
// ── 輔助函式 ──
void section(const std::string& title) {
std::cout << "\n========================================\n";
std::cout << " " << title << "\n";
std::cout << "========================================\n";
}
// ============================================================
// 1. 繼承 std::exception 的自訂例外
// ============================================================
class AppException : public std::exception {
public:
explicit AppException(const std::string& message)
: message_(message) {}
const char* what() const noexcept override {
return message_.c_str();
}
private:
std::string message_;
};
// ============================================================
// 2. 繼承 std::runtime_error 的自訂例外(更簡潔)
// ============================================================
class NetworkException : public std::runtime_error {
public:
NetworkException(const std::string& message, int errorCode)
: std::runtime_error(message), errorCode_(errorCode) {}
int errorCode() const { return errorCode_; }
std::string fullMessage() const {
return std::string(what()) + " (錯誤碼: " + std::to_string(errorCode_) + ")";
}
private:
int errorCode_;
};
// ============================================================
// 3. 帶有上下文資訊的例外
// ============================================================
class DetailedException : public std::runtime_error {
public:
DetailedException(const std::string& message,
const std::string& file,
int line,
const std::string& function)
: std::runtime_error(message),
file_(file), line_(line), function_(function) {}
const std::string& file() const { return file_; }
int line() const { return line_; }
const std::string& function() const { return function_; }
std::string detailedMessage() const {
std::ostringstream oss;
oss << what() << "\n"
<< " 位置: " << file_ << ":" << line_ << "\n"
<< " 函式: " << function_;
return oss.str();
}
private:
std::string file_;
int line_;
std::string function_;
};
// 巨集簡化使用
#define THROW_DETAILED(msg) \
throw DetailedException(msg, __FILE__, __LINE__, __func__)
// ============================================================
// 4. 計算器例外階層
// ============================================================
class MathException : public std::runtime_error {
public:
MathException(const std::string& msg, const std::string& operation)
: std::runtime_error(msg), operation_(operation) {}
const std::string& operation() const { return operation_; }
private:
std::string operation_;
};
class DivisionByZeroException : public MathException {
public:
DivisionByZeroException(double numerator)
: MathException("除以零", "division"),
numerator_(numerator) {}
double numerator() const { return numerator_; }
private:
double numerator_;
};
class NegativeSqrtException : public MathException {
public:
explicit NegativeSqrtException(double value)
: MathException("負數不能取平方根", "sqrt"),
value_(value) {}
double value() const { return value_; }
private:
double value_;
};
class OverflowException : public MathException {
public:
OverflowException(double a, double b)
: MathException("計算結果溢位", "multiply"),
a_(a), b_(b) {}
double a() const { return a_; }
double b() const { return b_; }
private:
double a_, b_;
};
// 計算器類別
class Calculator {
public:
static double divide(double a, double b) {
if (b == 0.0) {
throw DivisionByZeroException(a);
}
return a / b;
}
static double squareRoot(double x) {
if (x < 0) {
throw NegativeSqrtException(x);
}
return std::sqrt(x);
}
static double multiply(double a, double b) {
double result = a * b;
if (std::isinf(result)) {
throw OverflowException(a, b);
}
return result;
}
static double power(double base, int exp) {
if (base == 0 && exp < 0) {
throw DivisionByZeroException(0);
}
double result = std::pow(base, exp);
if (std::isinf(result)) {
throw OverflowException(base, exp);
}
return result;
}
};
// ============================================================
// 5. 銀行帳戶例外
// ============================================================
class BankException : public std::runtime_error {
public:
BankException(const std::string& msg, const std::string& accountId)
: std::runtime_error(msg), accountId_(accountId) {}
const std::string& accountId() const { return accountId_; }
private:
std::string accountId_;
};
class InsufficientFundsException : public BankException {
public:
InsufficientFundsException(const std::string& accountId,
double balance,
double requestedAmount)
: BankException("餘額不足", accountId),
balance_(balance), requestedAmount_(requestedAmount) {}
double balance() const { return balance_; }
double requestedAmount() const { return requestedAmount_; }
double shortfall() const { return requestedAmount_ - balance_; }
std::string detail() const {
std::ostringstream oss;
oss << what() << " — 帳戶 " << accountId()
<< ":餘額 $" << balance_
<< ",請求提取 $" << requestedAmount_
<< ",差額 $" << shortfall();
return oss.str();
}
private:
double balance_;
double requestedAmount_;
};
class AccountNotFoundException : public BankException {
public:
explicit AccountNotFoundException(const std::string& accountId)
: BankException("找不到帳戶", accountId) {}
};
class InvalidAmountException : public BankException {
public:
InvalidAmountException(const std::string& accountId, double amount)
: BankException("金額無效", accountId), amount_(amount) {}
double amount() const { return amount_; }
private:
double amount_;
};
// 銀行帳戶類別
class BankAccount {
public:
BankAccount(const std::string& id, const std::string& owner, double balance)
: id_(id), owner_(owner), balance_(balance) {}
void deposit(double amount) {
if (amount <= 0) {
throw InvalidAmountException(id_, amount);
}
balance_ += amount;
std::cout << " 存入 $" << amount << ",餘額 $" << balance_ << "\n";
}
void withdraw(double amount) {
if (amount <= 0) {
throw InvalidAmountException(id_, amount);
}
if (amount > balance_) {
throw InsufficientFundsException(id_, balance_, amount);
}
balance_ -= amount;
std::cout << " 提取 $" << amount << ",餘額 $" << balance_ << "\n";
}
void transfer(BankAccount& to, double amount) {
std::cout << " 轉帳 $" << amount << " 從 " << id_ << " 到 " << to.id_ << "\n";
withdraw(amount); // 可能拋出 InsufficientFundsException
try {
to.deposit(amount);
}
catch (...) {
balance_ += amount; // 回復(強烈保證)
throw;
}
}
std::string info() const {
std::ostringstream oss;
oss << "帳戶 " << id_ << " (" << owner_ << "): $" << balance_;
return oss.str();
}
double balance() const { return balance_; }
private:
std::string id_;
std::string owner_;
double balance_;
};
// ============================================================
// 6. noexcept 示範
// ============================================================
void noexceptFunc() noexcept {
// 保證不拋出例外
// 如果違反,程式呼叫 std::terminate
}
void mayThrowFunc() {
throw std::runtime_error("我會拋出例外");
}
class NoexceptDemo {
public:
NoexceptDemo() = default;
~NoexceptDemo() = default; // 解構子預設 noexcept
// 移動操作標記 noexcept — 讓 vector 能使用移動
NoexceptDemo(NoexceptDemo&&) noexcept = default;
NoexceptDemo& operator=(NoexceptDemo&&) noexcept = default;
NoexceptDemo(const NoexceptDemo&) = default;
NoexceptDemo& operator=(const NoexceptDemo&) = default;
};
int main() {
std::cout << "===== Ch18:自訂例外類別示範 =====\n";
// ──────────────────────────────────────────
// 1. 基本自訂例外
// ──────────────────────────────────────────
section("1. 繼承 std::exception");
{
try {
throw AppException("這是自訂的 AppException");
}
catch (const std::exception& e) {
std::cout << "捕捉到: " << e.what() << "\n";
}
}
// ──────────────────────────────────────────
// 2. 帶有額外資料的例外
// ──────────────────────────────────────────
section("2. 帶有額外資料的例外");
{
try {
throw NetworkException("連線逾時", 408);
}
catch (const NetworkException& e) {
std::cout << "捕捉到 NetworkException:\n";
std::cout << " 訊息: " << e.what() << "\n";
std::cout << " 錯誤碼: " << e.errorCode() << "\n";
std::cout << " 完整訊息: " << e.fullMessage() << "\n";
}
}
// ──────────────────────────────────────────
// 3. 帶有位置資訊的例外
// ──────────────────────────────────────────
section("3. 帶有位置資訊的例外");
{
try {
THROW_DETAILED("偵測到無效狀態");
}
catch (const DetailedException& e) {
std::cout << "捕捉到 DetailedException:\n";
std::cout << e.detailedMessage() << "\n";
}
}
// ──────────────────────────────────────────
// 4. 計算器例外階層
// ──────────────────────────────────────────
section("4. 計算器例外階層");
{
struct TestCase {
std::string description;
std::function<double()> operation;
};
std::vector<TestCase> tests = {
{"10 / 3", []() { return Calculator::divide(10, 3); }},
{"10 / 0", []() { return Calculator::divide(10, 0); }},
{"sqrt(25)", []() { return Calculator::squareRoot(25); }},
{"sqrt(-4)", []() { return Calculator::squareRoot(-4); }},
{"1e308 * 2", []() { return Calculator::multiply(1e308, 2); }},
{"0 ^ -1", []() { return Calculator::power(0, -1); }},
};
for (const auto& test : tests) {
try {
double result = test.operation();
std::cout << test.description << " = " << result << "\n";
}
catch (const DivisionByZeroException& e) {
std::cout << test.description << " → [DivisionByZero] "
<< e.what() << " (分子=" << e.numerator() << ")\n";
}
catch (const NegativeSqrtException& e) {
std::cout << test.description << " → [NegativeSqrt] "
<< e.what() << " (值=" << e.value() << ")\n";
}
catch (const OverflowException& e) {
std::cout << test.description << " → [Overflow] "
<< e.what() << "\n";
}
catch (const MathException& e) {
std::cout << test.description << " → [MathException] "
<< e.what() << " (操作: " << e.operation() << ")\n";
}
}
}
// ──────────────────────────────────────────
// 5. 銀行帳戶系統
// ──────────────────────────────────────────
section("5. 銀行帳戶系統");
{
BankAccount alice("A001", "Alice", 1000.0);
BankAccount bob("B002", "Bob", 500.0);
std::cout << "初始狀態:\n";
std::cout << " " << alice.info() << "\n";
std::cout << " " << bob.info() << "\n\n";
// 正常存款
std::cout << "--- Alice 存入 $200 ---\n";
try {
alice.deposit(200);
}
catch (const BankException& e) {
std::cout << " 錯誤: " << e.what() << "\n";
}
// 正常提款
std::cout << "\n--- Alice 提取 $300 ---\n";
try {
alice.withdraw(300);
}
catch (const BankException& e) {
std::cout << " 錯誤: " << e.what() << "\n";
}
// 餘額不足
std::cout << "\n--- Bob 嘗試提取 $1000 ---\n";
try {
bob.withdraw(1000);
}
catch (const InsufficientFundsException& e) {
std::cout << " " << e.detail() << "\n";
}
// 無效金額
std::cout << "\n--- Alice 嘗試存入 $-50 ---\n";
try {
alice.deposit(-50);
}
catch (const InvalidAmountException& e) {
std::cout << " 錯誤: " << e.what()
<< "(帳戶 " << e.accountId()
<< ",金額 $" << e.amount() << ")\n";
}
// 正常轉帳
std::cout << "\n--- Alice 轉帳 $200 給 Bob ---\n";
try {
alice.transfer(bob, 200);
}
catch (const BankException& e) {
std::cout << " 轉帳失敗: " << e.what() << "\n";
}
// 轉帳餘額不足
std::cout << "\n--- Bob 嘗試轉帳 $2000 給 Alice ---\n";
try {
bob.transfer(alice, 2000);
}
catch (const InsufficientFundsException& e) {
std::cout << " " << e.detail() << "\n";
}
std::cout << "\n最終狀態:\n";
std::cout << " " << alice.info() << "\n";
std::cout << " " << bob.info() << "\n";
}
// ──────────────────────────────────────────
// 6. 例外階層的多型捕捉
// ──────────────────────────────────────────
section("6. 例外階層的多型捕捉");
{
std::cout << "銀行例外階層:\n";
std::cout << R"(
BankException (繼承 std::runtime_error)
├── InsufficientFundsException
├── AccountNotFoundException
└── InvalidAmountException
)";
std::cout << "\n不同層級的捕捉:\n";
auto throwTest = []() {
throw InsufficientFundsException("X001", 100, 500);
};
// 捕捉具體型別
try { throwTest(); }
catch (const InsufficientFundsException& e) {
std::cout << " InsufficientFundsException: "
<< e.what() << " (差額 $" << e.shortfall() << ")\n";
}
// 捕捉中間基底類別
try { throwTest(); }
catch (const BankException& e) {
std::cout << " BankException: " << e.what()
<< " (帳戶: " << e.accountId() << ")\n";
}
// 捕捉 std::exception
try { throwTest(); }
catch (const std::exception& e) {
std::cout << " std::exception: " << e.what() << "\n";
}
}
// ──────────────────────────────────────────
// 7. noexcept 規範
// ──────────────────────────────────────────
section("7. noexcept 規範");
{
std::cout << "noexcept 檢查:\n";
std::cout << " noexceptFunc: "
<< (noexcept(noexceptFunc()) ? "noexcept" : "可拋出") << "\n";
std::cout << " mayThrowFunc: "
<< (noexcept(mayThrowFunc()) ? "noexcept" : "可拋出") << "\n";
// 移動操作的 noexcept
std::cout << "\nNoexceptDemo 的移動建構子: "
<< (noexcept(NoexceptDemo(std::declval<NoexceptDemo&&>()))
? "noexcept" : "可拋出") << "\n";
std::cout << "\nnoexcept 重要規則:\n";
std::cout << " 1. 解構子預設是 noexcept\n";
std::cout << " 2. 移動操作應標記 noexcept\n";
std::cout << " 3. swap 函式應標記 noexcept\n";
std::cout << " 4. noexcept 函式若拋出 → std::terminate\n";
// 條件式 noexcept
std::cout << "\n條件式 noexcept 示範:\n";
std::cout << R"(
template<typename T>
void swap(T& a, T& b) noexcept(noexcept(T(std::move(a)))) {
T temp = std::move(a);
a = std::move(b);
b = std::move(temp);
}
// → 如果 T 的移動建構是 noexcept,swap 也是 noexcept
)";
}
// ──────────────────────────────────────────
// 8. 例外安全保證
// ──────────────────────────────────────────
section("8. 例外安全保證");
{
std::cout << "三個層級的例外安全保證:\n\n";
std::cout << "【不拋出保證(No-throw Guarantee)】\n";
std::cout << " 操作保證不會拋出例外\n";
std::cout << " 適用:解構子、移動操作、swap\n";
std::cout << " 標記:noexcept\n\n";
std::cout << "【強烈保證(Strong Guarantee)】\n";
std::cout << " 操作若失敗,狀態回復到呼叫前(如同資料庫的交易)\n";
std::cout << " 實作方式:Copy-and-Swap 慣用法\n";
std::cout << R"(
MyClass& operator=(const MyClass& other) {
MyClass temp(other); // 複製(可能拋出)
swap(*this, temp); // 交換(noexcept)
return *this;
} // temp 釋放舊資源(noexcept)
)";
std::cout << "\n【基本保證(Basic Guarantee)】\n";
std::cout << " 操作若失敗,物件仍處於有效狀態\n";
std::cout << " 但內容可能已改變(不保證回復原狀)\n";
std::cout << " 最低要求:不洩漏資源、不留下無效狀態\n";
// 強烈保證的示範:BankAccount::transfer
std::cout << "\n--- BankAccount::transfer 具有強烈保證 ---\n";
BankAccount a("T1", "Test-A", 100);
BankAccount b("T2", "Test-B", 0);
std::cout << "轉帳前:A=$" << a.balance() << ", B=$" << b.balance() << "\n";
try {
a.transfer(b, 200); // 餘額不足
}
catch (const InsufficientFundsException&) {
std::cout << "轉帳失敗(餘額不足)\n";
}
std::cout << "轉帳後:A=$" << a.balance() << ", B=$" << b.balance() << "\n";
std::cout << "→ 兩個帳戶的餘額都沒有改變(強烈保證 ✓)\n";
}
// ──────────────────────────────────────────
// 9. 例外設計最佳實踐
// ──────────────────────────────────────────
section("9. 例外設計最佳實踐");
{
std::cout << "1. 總是以 const& 捕捉例外\n";
std::cout << "2. 繼承自 std::exception 或其子類別\n";
std::cout << "3. 覆寫 what() 回傳有意義的訊息\n";
std::cout << "4. 攜帶足夠的上下文資訊(錯誤碼、檔案名、行號等)\n";
std::cout << "5. 設計合理的例外階層(不要太深也不要太平)\n";
std::cout << "6. 解構子永遠不要拋出例外\n";
std::cout << "7. 移動操作標記 noexcept\n";
std::cout << "8. 用 throw; 重新拋出(保留型別資訊)\n";
std::cout << "9. 例外用於「異常」情況,不用於正常控制流程\n";
std::cout << "10. 考慮例外安全性(至少提供基本保證)\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 →