异常(Exceptions)

重要的事情说三遍:

强烈建议按照目录结构中的顺序学习!!!点我查看教程目录结构

强烈建议按照目录结构中的顺序学习!!!点我查看教程目录结构

强烈建议按照目录结构中的顺序学习!!!点我查看教程目录结构

异常提供了一种在程序中应对异常情况(如运行时错误)的方法,通过将控制转移到特殊函数(称为处理程序)来实现。

要捕获异常,可以将一部分代码放在异常检查之下。这是通过将这部分代码放在 try 块中来实现的。当在该块中出现异常情况时,会抛出一个异常,将控制转移到异常处理程序。如果没有抛出异常,代码将正常继续执行,所有处理程序都将被忽略。

异常通过在 try 块内使用 throw 关键字来抛出。异常处理程序使用关键字 catch 声明,必须紧接在 try 块后面:

// exceptions
#include <iostream>
using namespace std;

int main () {
  try
  {
    throw 20;
  }
  catch (int e)
  {
    cout << "发生了一个异常。异常编号:" << e << '\n';
  }
  return 0;
}

处理异常的代码被包裹在 try 块中。在这个例子中,这段代码只是抛出了一个异常:

throw 20;

throw 表达式接受一个参数(在本例中是整数值 20),该参数作为参数传递给异常处理程序。

异常处理程序使用 catch 关键字声明,紧接在 try 块的右括号之后。catch 的语法类似于一个带有一个参数的常规函数。这个参数的类型非常重要,因为 throw 表达式传递的参数类型将与它进行检查,只有在它们匹配的情况下,异常才会被该处理程序捕获。

可以将多个处理程序(即 catch 表达式)链接在一起;每个处理程序带有不同的参数类型。只有参数类型与 throw 语句中指定的异常类型匹配的处理程序会被执行。

如果使用省略号(...)作为 catch 的参数,则该处理程序将捕获任何异常,无论抛出的异常类型是什么。这可以用作捕获所有未被其他处理程序捕获的异常的默认处理程序:

try {
  // code here
}
catch (int param) { cout << "int 异常"; }
catch (char param) { cout << "char 异常"; }
catch (...) { cout << "默认异常"; }

在这种情况下,最后一个处理程序将捕获任何既不是 int 也不是 char 类型的异常。

处理完异常后,程序的执行将从 try-catch 块之后恢复,而不是从 throw 语句之后恢复!

也可以在更外层的 try 块中嵌套 try-catch 块。在这些情况下,我们有可能内部 catch 块将异常转发到其外部级别。这可以通过不带参数的 throw; 表达式来完成。例如:

try {
  try {
      // code here
  }
  catch (int n) {
      throw;
  }
}
catch (...) {
  cout << "发生了异常";
}

异常规范

旧代码中可能包含动态异常规范。它们现在在 C++ 中已被弃用,但仍受支持。动态异常规范在函数声明后附加一个 throw 说明符。例如:

double myfunction (char param) throw (int);

这声明了一个名为 myfunction 的函数,它接受一个类型为 char 的参数并返回一个类型为 double 的值。如果该函数抛出 int 以外类型的异常,函数将调用 std::unexpected 而不是寻找处理程序或调用 std::terminate

如果 throw 说明符留空没有类型,这意味着对于任何异常都会调用 std::unexpected。没有 throw 说明符的函数(常规函数)永远不会调用 std::unexpected,而是遵循正常路径寻找其异常处理程序。

int myfunction (int param) throw(); // 所有异常调用 unexpected
int myfunction (int param);         // 正常异常处理 

标准异常

C++ 标准库提供了一个基类,专门用于声明对象以作为异常抛出。它被称为 std::exception,在头文件 <exception> 中定义。该类具有一个虚成员函数 what,返回一个空终止的字符序列(类型为 char *),并且可以在派生类中重写以包含某种描述异常的信息。

// 使用标准异常
#include <iostream>
#include <exception>
using namespace std;

class myexception: public exception
{
  virtual const char* what() const throw()
  {
    return "发生了我的异常";
  }
} myex;

int main () {
  try
  {
    throw myex;
  }
  catch (exception& e)
  {
    cout << e.what() << '\n';
  }
  return 0;
}

我们放置了一个通过引用捕获异常对象的处理程序(注意类型后的 &),因此这也捕获从 exception 派生的类,如我们类型为 myexceptionmyex 对象。

C++ 标准库的所有组件抛出的异常都派生自 exception 类。这些包括:

exceptiondescription
bad_allocnew 在分配失败时抛出
bad_castdynamic_cast 在动态转换失败时抛出
bad_exception由某些动态异常规范抛出
bad_typeidtypeid 抛出
bad_function_call由空的 function 对象抛出
bad_weak_ptrshared_ptr 在传递一个无效的 weak_ptr 时抛出

同样派生自 exception,头文件 <exception> 定义了两种通用的异常类型,这些类型可以被自定义异常继承以报告错误:

exceptiondescription
logic_error与程序内部逻辑相关的错误
runtime_error在运行时检测到的错误

一个典型的需要检查标准异常的例子是内存分配:

// bad_alloc 标准异常
#include <iostream>
#include <exception>
using namespace std;

int main () {
  try
  {
    int* myarray = new int[1000];
  }
  catch (exception& e)
  {
    cout << "标准异常:" << e.what() << endl;
  }
  return 0;
}

在这个例子中,异常处理程序可能捕获的异常是 bad_alloc。由于 bad_alloc 派生自标准基类 exception,因此它可以被捕获(通过引用捕获,捕获所有相关类)。

  • 24
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值