异常是什么
概念上,异常是一种处理错误的方式。当一个函数遇到自己无法处理的错误时,会抛出异常,交由调用者处理。
在 C 语言中,我们主要通过错误码来处理错误。错误码本质是对错误信息进行编号,拿到码后还得去查表获取具体信息,比较麻烦。而 C++ 的异常机制则更直接,通过对象传递错误信息。
使用异常主要涉及三个关键字:
throw: 当函数出现问题时抛出异常 try: 包含可能出错的代码块,通常跟随 catch 模块 catch: 用来捕获并处理异常
栈展开
抛出异常后,程序会暂停当前函数的执行,开始寻找匹配的 catch 子句。首先检查 throw 是否在 try 块内部,如果在则查找匹配的 catch 语句;如果有匹配,则跳转到 catch 处处理。
如果当前函数中没有 try/catch 子句,或者类型不匹配,则退出当前函数,继续在外层调用链中查找。这种逐层向上查找的过程叫做栈展开(Stack Unwinding)。
如果一直找到 main 函数仍未找到匹配的 catch 子句,程序会调用标准库的 terminate 函数终止程序;如果找到了,处理完 catch 代码后,程序会继续执行后续逻辑。
异常的抛出和捕获
当程序出现问题时,我们会抛出一个对象来引发异常。该对象的类型以及当前的调用链决定了由哪个 catch 处理代码来接管。
被选中的处理代码是调用链中与该对象类型匹配且离抛出位置最近的那一个。根据抛出对象的类型和内容,异常处理部分能获知具体的错误情况。

类型匹配规则
- 类型不匹配: 如果类型不匹配,会继续向上传递。

- 执行流跳转: 当
throw执行时,其后的语句将不再被执行。程序直接从 throw 位置跳到匹配的 catch 模块。 - 通配符捕获: 使用
catch(...)可以捕获任意类型的异常,但缺点是不知道具体是什么错误。 - 拷贝机制: 抛出异常对象后,会生成一个异常对象的拷贝。因为抛出的可能是临时对象,所以必须生成拷贝,这个拷贝对象会在 catch 子句结束后销毁。
异常的重新抛出
有时我们需要对捕获到的异常进行分类处理。比如某种特定错误需要特殊处理,而其他错误则应重新抛出给外层调用链。
下面模拟一个聊天发消息的场景:网络不好时需要多次重试,其他错误则直接上报。
class Exception {
public:
Exception(const string& errmsg, int id) : _errmsg(errmsg), _id(id) {}
virtual string { _errmsg; }
{ _id; }
:
string _errmsg;
_id;
};
: Exception {
:
( string& errmsg, id, string type)
: (errmsg, id), _type(type) {}
{
string str = ;
str += _type;
str += ;
str += _errmsg;
str;
}
:
string _type;
};
_SeedMsg( string& s) {
(() % == ) {
(, , );
} (() % == ) {
(, , );
} {
cout << << endl;
}
}
{
( i = ; i < ; i++) {
{
_SeedMsg(s);
;
} ( Exception& e) {
(e.() == ) {
(i == ) ;
cout << << i + << << endl;
} {
;
}
}
}
}
{
(());
string str;
(cin >> str) {
{
(str);
} ( Exception& e) {
cout << e.() << endl << endl;
} (...) {
cout << << endl;
}
}
;
}




