1. exceptionprocessingoverview
1.1 what is exception?
exception is 程序run过程in发生 特殊circumstances, such as除以零, array越界, memory不足etc.. 这些circumstances可能导致程序崩溃 or 产生error结果.
in C++in, exceptionprocessingproviding了一种mechanism, 使得程序able to优雅地processing这些exceptioncircumstances, 而不 is 直接崩溃.
1.2 exceptionprocessing 优势
- 分离errorprocessingcode: 将errorprocessingcode and 正常业务逻辑分离, improvingcode readable 性 and 可maintenance性.
- 传递errorinformation: 可以将errorinformation from 发生error 地方传递 to able toprocessing该error 地方.
- 统一errorprocessing: providing了一种统一 errorprocessingmechanism, 适用于不同class型 error.
- resourcemanagement: 结合RAII (resource获取即初始化) mechanism, 可以确保resource in exception发生时正确释放.
2. exceptionprocessing basic语法
2.1 抛出exception
usingthrow关键字抛出exception:
throw exception值;
exception值可以 is 任何class型, includingbasicclass型, classobjectetc..
2.2 捕获exception
usingtry-catch语句捕获exception:
try {
// 可能抛出exception code
} catch (exceptionclass型1 exceptionvariable) {
// processingexceptionclass型1 code
} catch (exceptionclass型2 exceptionvariable) {
// processingexceptionclass型2 code
} catch (...) {
// processingotherclass型exception code
}
2.3 exceptionprocessingexample
#include <iostream>
#include <string>
using namespace std;
int divide(int a, int b) {
if (b == 0) {
throw string("除数不能 for 零");
}
return a / b;
}
int main() {
try {
int result = divide(10, 0);
cout << "结果: " << result << endl;
} catch (string e) {
cout << "捕获 to exception: " << e << endl;
} catch (...) {
cout << "捕获 to 未知exception" << endl;
}
cout << "程序继续执行" << endl;
return 0;
}
run结果:
捕获 to exception: 除数不能 for 零 程序继续执行
3. exception 传播
3.1 exception 栈unfold
当exception被抛出但 in 当 before functionin没 has 被捕获时, exception会沿着调用栈向 on 传播, 直 to 找 to able to捕获该exception catch块. 这个过程称 for 栈unfold (stack unwinding) .
in 栈unfold过程in, 会自动销毁 in exception发生点之 after creation 局部object.
3.2 exception传播example
#include <iostream>
using namespace std;
void func3() {
cout << "进入func3" << endl;
throw 100; // 抛出exception
cout << "离开func3" << endl; // 不会执行
}
void func2() {
cout << "进入func2" << endl;
func3();
cout << "离开func2" << endl; // 不会执行
}
void func1() {
cout << "进入func1" << endl;
func2();
cout << "离开func1" << endl; // 不会执行
}
int main() {
cout << "进入main" << endl;
try {
func1();
} catch (int e) {
cout << "捕获 to exception: " << e << endl;
}
cout << "离开main" << endl;
return 0;
}
run结果:
进入main 进入func1 进入func2 进入func3 捕获 to exception: 100 离开main
4. exception层次structure
4.1 标准exceptionclass
C++标准libraryproviding了一系列exceptionclass, 它们都inheritance自基classexception. 这些exceptionclass定义 in 头file<stdexcept>in.
4.1.1 common 标准exceptionclass
- std::exception: 所 has 标准exception 基class
- std::bad_alloc: memory分配失败时抛出
- std::bad_cast: class型转换失败时抛出
- std::bad_typeid: usingtypeidoperation符时抛出
- std::logic_error: 逻辑error, such asparameter无效
- std::runtime_error: run时error, such as除以零
4.2 自定义exceptionclass
可以throughinheritanceexceptionclass or 其forkclass来creation自定义exceptionclass:
#include <iostream>
#include <exception>
#include <string>
using namespace std;
class MyException : public exception {
private:
string message;
public:
MyException(const string& msg) : message(msg) {}
const char* what() const noexcept override {
return message.c_str();
}
};
void func() {
throw MyException("自定义exception");
}
int main() {
try {
func();
} catch (const exception& e) {
cout << "捕获 to exception: " << e.what() << endl;
}
return 0;
}
5. exceptionsecurity
5.1 exceptionsecurity concepts
exceptionsecurity is 指程序 in 发生exception时, able to保持object处于 has 效status, will not causeresource泄漏 or data损 bad .
5.2 exceptionsecurity级别
- 无抛出保证 (nothrow guarantee) : function承诺不会抛出任何exception.
- 强exceptionsecurity保证 (strong exception safety guarantee) : such as果function抛出exception, 程序status不会改变, 相当于function from 未被调用过.
- basicexceptionsecurity保证 (basic exception safety guarantee) : such as果function抛出exception, 程序status仍然 has 效, 但可能 and 调用 before 不同.
- 无exceptionsecurity保证: function抛出exception时, 程序可能处于无效status.
5.3 RAII and exceptionsecurity
RAII (Resource Acquisition Is Initialization, resource获取即初始化) is aC++programmingtechniques, 它将resource 获取 and object 初始化绑定, 将resource 释放 and object 销毁绑定.
RAII可以确保即使 in exception发生时, resource也能被正确释放:
#include <iostream>
#include <memory>
using namespace std;
class Resource {
public:
Resource() {
cout << "获取resource" << endl;
}
~Resource() {
cout << "释放resource" << endl;
}
void use() {
cout << "usingresource" << endl;
throw runtime_error("usingresource时发生error");
}
};
int main() {
try {
// using智能指针managementresource
unique_ptr res(new Resource());
res->use();
} catch (const exception& e) {
cout << "捕获 to exception: " << e.what() << endl;
}
// 即使发生exception, resource也会被正确释放
cout << "程序继续执行" << endl;
return 0;
}
6. exceptionprocessing best practices
6.1 whenusingexception
- 用于processing程序无法正常继续执行 严重error.
- 用于processing不common但可能发生 errorcircumstances.
- 用于跨 many 个function层次传递errorinformation.
6.2 when不usingexception
- 用于processing程序可以正常processing 预期circumstances.
- in performance关键 codein, 因 for exceptionprocessing会带来一定 performance开销.
- in 底层systemcodein, such asoperationsystem in 核.
6.3 exceptionprocessing best practices
- using具体 exceptionclass型: 尽量using具体 exceptionclass型, 而不 is common
exception. - 捕获适当 exception: 只捕获你able toprocessing exception.
- usingRAIImanagementresource: 确保resource in exception发生时able to被正确释放.
- 不要 in 析构functionin抛出exception: 析构function应该 is noexcept , 否则可能导致程序终止.
- providing has 意义 exceptioninformation: in exceptioninpackage含足够 information, 以便于debug.
- usingexception规范 (C++11 before ) or noexcept (C++11及以 after ) : 明确function exceptionbehavior.
实践case: 银行account余额management
writing一个C++程序, implementation银行account 余额management, including存款, 取款functions, 并usingexceptionprocessing来processing余额不足 circumstances.
requirementsanalysis
- creation一个
BankAccountclass, package含账号, 余额etc.property. - implementation
depositmethod用于存款. - implementation
withdrawmethod用于取款, 当余额不足时抛出exception. - implementationexceptionprocessing, 捕获并processing余额不足 exception.
referencecode
#include <iostream>
#include <string>
#include <exception>
using namespace std;
class InsufficientFundsException : public exception {
private:
string message;
public:
InsufficientFundsException(double balance, double amount) {
message = "余额不足: 当 before 余额 " + to_string(balance) + ", 尝试取款 " + to_string(amount);
}
const char* what() const noexcept override {
return message.c_str();
}
};
class BankAccount {
private:
string accountNumber;
double balance;
public:
BankAccount(const string& accNum, double initialBalance) :
accountNumber(accNum), balance(initialBalance) {}
void deposit(double amount) {
if (amount > 0) {
balance += amount;
cout << "存款成功: " << amount << ", 当 before 余额: " << balance << endl;
}
}
void withdraw(double amount) {
if (amount > balance) {
throw InsufficientFundsException(balance, amount);
}
balance -= amount;
cout << "取款成功: " << amount << ", 当 before 余额: " << balance << endl;
}
double getBalance() const {
return balance;
}
string getAccountNumber() const {
return accountNumber;
}
};
int main() {
try {
BankAccount acc("123456", 1000.0);
acc.deposit(500.0);
acc.withdraw(800.0);
acc.withdraw(1000.0); // 这会抛出exception
} catch (const InsufficientFundsException& e) {
cout << "捕获 to exception: " << e.what() << endl;
} catch (const exception& e) {
cout << "捕获 to otherexception: " << e.what() << endl;
}
cout << "程序继续执行" << endl;
return 0;
}
run结果
存款成功: 500, 当 before 余额: 1500 取款成功: 800, 当 before 余额: 700 捕获 to exception: 余额不足: 当 before 余额 700, 尝试取款 1000 程序继续执行
互动练习
练习1: writing一个C++function, 计算两个整数 除法, 当除数 for 零时抛出exception. in mainfunctionin捕获并processing这个exception.
练习2: creation一个自定义exceptionclassNegativeNumberException, 当尝试计算负数 平方根时抛出此exception.
练习3: modify实践casein BankAccountclass, 添加转账functions, 并processing可能 exceptioncircumstances.