C++ 异常处理
异常是程序在执行期间产生的问题。C++ 异常是指在程序运行时发生的特殊情况,比如尝试除以零的操作。
处理异常的关键字
异常提供了一种转移程序控制权的方式。C++ 异常处理涉及到三个关键字:try、catch、throw。
-
throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。
-
catch: 在想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。catch的形参可以是对象,也可以是引用,也可以是指针。
-
try: try 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。
例如对于下面的程序
// 处理divide函数可能抛出的异常
try {
divide(8, 2);
} catch (runtime_error& err) {
cout << err.what() << endl;
exit(1);
}
// 有异常处理版本
int divide(int d1, int d2) {
if (d2 == 0)
throw runtime_error(“d2 is zero!”); // 抛出异常
// throw 0; //只是为了展示内置类型作为异常
return d1 / d2;
}
通常格式为
try
{
// 保护代码
}catch( ExceptionName e1 ){
// 处理 e1 异常的代码
}catch( ExceptionName e2){
// 处理 e2 异常的代码
}
...
}catch(...){//捕获一切异常,注意处理异常代码的顺序
//处理异常 至少应该纪录日志文件
}
注意异常处理代码最后不需要break语句,同时异常处理可以嵌套,也不必在一个程序内完成对异常的全部处理,可以利用throw交给更高层的异常处理程序。
try {
divide(8, 2);
} catch (runtime_error& err) {
cout << err.what() << endl;
throw; //完成部分处理,然后重新抛出异常,不必一次处理完
}
异常处理的过程
异常对象
标准异常
- bad_cast:dynamic_cast 转换目标为引用类型,转换失败
- bad_alloc:不能分配所要求的内存空间
- runtime_error:只有在运行时才能检测出的问题
- overflow_error:计算上溢
- underflow_error:计算下溢
- range_error:生成的结果超出了有意义的值域范围
- logic_error:程序逻辑错误
- domain_error:执行一段程序所需要的先决条件不满足
- invalid_argument:无效参数
- out_of _range:使用一个超出有效范围的值
- length_error:试图创建一个超出该类型最大长度的对象
虚成员函数: virtual const char* what() const noexcept;
//noexcept保证该函数不会抛出异常,what() 是异常类提供的一个公共方法,已被所有子异常类重载,将返回异常产生的原因。
自定义异常类
实际应用时的应用程序通常会自定义异常类,可以通过继承和重载exception来定义新的异常
class MyExcepation : public runtime_error {
public:
explicit MyExcepation(const string &s) : runtime_error(s){}//继承
const char* what() const {//重写
return "MyExcepation";
}
};
异常对象
异常对象是一种特殊的对象,编译器使用异常抛出throw表达式来对
异常对象进行拷贝初始化
throw runtime_error("error");
异常对象位于编译器管理的空间中,以确保无论最终调用的是哪个
catch 子句,都能访问该空间。当异常处理完毕后,异常对象被销毁。如果该表达式是类类型,则该类必须有一个可用的拷贝构造函数和析构函数。
注意,不要在throw语句中抛出局部变量的指针,极其容易出错。
抛出一个表达式时,该表达式的静态编译类型将决定异常对象的类型,故当throw的是基类指针时,而该指针指向的对象为派生类,将对派生类进行切割,只有基类的部分被切割。
异常类型的匹配
在异常和catch异常声明的匹配中,绝大多数
的类型转换不被允许,只允许以下几种:
- 从非const类型到const类型的类型转换
- 从派生类到基类的类型转换
- 数组被转换成指向数组类型的指针,函数被转换
成指向该函数类型的指针
析构函数与异常机制
- C++不禁止析构函数向外界抛出异常,但析构函
数被期望不向外界函数抛出异常,析构函数中向
函数外抛出异常,将直接调用terminator()系统函
数终止程序 - 如果一个析构函数内部抛出了异常,就应该在析
构函数的内部捕获并处理该异常,不应该让异常
被抛出析构函数之外 - 所有标准类库都能确保它们的析构函数不会引发
异常
禁止异常处理机制
使用 noexcept关键字,明确本函数不会抛出异常,若该函数运行时抛出异常,将终止程序的执行,可用于阻止异常的传播
int div(int d1, int d2) noexcept
{
if (d2 == 0)
//编译器不检查noexcept的函数是不是含有抛出异常的语句
throw runtime_error("d2 is zero!");
return d1/d2;
}

浙公网安备 33010602011771号