跳转至

异常处理

引入

异常就是在程序执行的时候出现的问题,也就是一些特殊的情况,这时候如果不进行处理很显然就会 Runtime Error,因此异常处理就显得尤为重要。

C++ 的异常处理的关键字为:trycatchthrow。语法如下所示:

try {
  // 这是一段保护代码
} catch (Exception e) {
  // 捕获 Exception 类型的代码块
  // 如果不需要知道异常产生的原因就不用写变量名
} catch (...) {
  // 捕获所有的异常
  // 也就是未知异常
}

抛出异常

你可以使用 throw 来抛出一个异常。如果嵌套了多层函数的话,那么异常并不会一层一层地回退回去,而是直接退到捕获区那里。例如,可以这样子抛出一个 string 类型的异常:

throw string("好渴鹅吃鱼鱼失败!")

捕获异常

catch 块里面的语句就是捕获了异常之后要执行的语句。例如这样子:

void eatFish() {
  throw string("好渴鹅吃鱼鱼失败!");
}

int main() {
  try {
    eatFish();
  } catch (string e) {
    cout << "Error: " << e << '\n';
    return 1;
  }
  return 0;
}

标准中的异常

C++ 已经在头文件 exception 当中提供了一些标准异常,我们可以直接在自己的程序里面使用这些标准的异常,也可以以继承的方式定义自己的异常。这是一段定义新的异常的代码:

#include <iostream>
#include <exception>

using namespace std;

class DivByZero {
  const char *what() const throw() {
    return "除数为零,无法相除!"
  }
};

void div(int a, int b) {
  if (!b) {
    throw DivByZero();
  }
  return a / b;
}

int main() {
  try {
    c = div(1, 0);
  } catch (DivByZero &e) {
    cout << e.what() << '\n';
  }
  return 0;
}

看到这里,你是不是已经初步掌握了 C++ 异常的处理方法了呢?

该不该使用异常

到底该不该在你的程序中使用异常处理呢?我想大概率是应该的。很多人说异常会拖慢程序的速度,还不如使用错误码。但是异常对于新手好,可读性高,错误码只是一个数字,不打注释别人根本就不知道你出现了什么错误?况且我们又不是造火箭,没有必要追求过高的速度。况且 STL 也都使用了异常处理:

_NODISCARD _CONSTEXPR20 const _Ty& at(const size_type _Pos) const {
  auto& _My_data = _Mypair._Myval2;
  if (static_cast<size_type>(_My_data._Mylast - _My_data._Myfirst) <= _Pos) {
    _Xrange();
  }
    return _My_data._Myfirst[_Pos];
}

这是 vector 的源代码,其中,在 _XRange 函数里面就抛出了异常:

[[noreturn]] static void _Xrange() {
  _Xout_of_range("invalid vector subscript");
}

所以对于长点的代码,还是老老实实使用异常吧!

评论