首页 > 编程语言 >C++ 异常

C++ 异常

时间:2023-03-16 14:34:26浏览次数:52  
标签:C++ try error catch runtime 异常 throw

C++ 异常 exception

《C++ Primer 中文版第五版》 Ch 5.6

1. try 语句块和异常处理

  • 典型的异常
    • 失去数据库连接
    • 遇到意外输入

如果程序的问题是输入无效,则异常处理部分可能会要求用户重新输入正确的数据;

如果丢失了数据库连接,会发出报警信息

  • C++ 中的异常处理包括:
    • throw 表达式(throw expression)
      • 异常检测部分使用throw 表达式来表示它遇到了无法处理的问题。我们说 throw 引发( raise )了异常。
    • try 语句块(try block)
      • 以 关键字 try 开始,并以一个或多个 catch 子句(catch clause) 结束
      • try 语句块中代码抛出的异常通常会被某个 catch 子句处理。因为 catch 子句 “处理” 异常,所以它们也被称作 异常处理代码( exception handler )
    • 一套 异常类(exception class)
      • 用于在 throw 表达式和相关的 catch 子句之间传递异常的具体信息

1.1 throw 表达式

throw + 表达式

表达式类型就是抛出的异常类型

  • 代码示例:

    Sales_item item1, item2;
    cin >> item1 >> item2;
    // 首先检查 item1 和 item2 是否表示同一种书籍
    if (item1.isbn() == item2.isbn())
    {
        cout << item1 + item2 << endl;
        return 0;	// 表示成功
    } else {
        cerr << "Data must refer to same ISBN" << endl;
        return -1;	// 表示失败
    }
    

真实的程序中,应该把对象相加的代码和用户交互的代码分离开来。

改写程序,不在直接输出一条信息,而是抛出一个异常:

// 首先检查两条数据是否是关于同一种书籍的
if (item1.isbn() != item2.isbn())
{
	throw runtime_error("Data must refer to same ISBN");
}
// 如果程序执行到了这里,表示两个 ISBN 是相同的
cout << item1 + item2 << endl;

这里如果 ISBN 不一样就抛出一个异常,该异常是类型 runtime_error 的对象。

抛出异常将终止当前的函数,并把控制权交给能处理该异常的代码

runtime_error标准库异常类型 的一种,定义在 stdexcept 头文件中。必须初始化 runtime_error 的对象,方式是提供给它一个 string 对象或者一个 C 风格的字符串,这个字符串有一些关于异常的辅助信息。

1.2 try 语句块

  • 语法

    try
    {
        program-statements
    } catch (exception-declaration) {
        hadler-statements
    } catch (exception-declaration) {
        hadler-statements
    } // ...
    

    异常声明 exception declaration

1.2.1 编写处理代码

假设执行 Sales_item 对象加法的代码是与用户交互的代码 分离开来的。其中与用户交互的代码负责处理发生的异常,它的形式可能如下:

while (cin >> item1 >> item2)
{
    try 
    {
        // 执行添加两个 Sales_item 对象的代码
        // 如果添加失败,代码抛出一个 runtime_error 异常
    } catch (runtime_error err) {
        // 提醒用户两个 ISBN 必须一致,询问是否重新输入
        cout << err.what()
            << "\nTry Again? Enter y or n" << endl;
        char c;
        cin >> c;
        if (!cin || c == 'n')
            break;		// 跳出 while 循环
    }
}

给用户的提示信息中输出了 err.what() 的返回值。err 的类型是 runtime_error ,因此能推断 what 是 runtime_error 类的一个成员函数。

每个标准库异常类都定义了名为 what 的成员函数,这些函数没有参数,返回值是 C 风格字符串(即 const char*)。其中,runtime_error 的 what 成员返回的是 初始化一个具体对象时所用的 string 对象的副本。如果上一节的代码抛出异常,则本节的 catch 子句输出:

Data must refer to same ISBN
Try Again? Enter y or n

1.2.2 函数在寻找处理代码的过程中退出

一个 try 包含另一个 try,新的 try 可能又包含了另一个 try 语句块的新函数,以此类推

寻找处理代码的过程与此相反

  • 当异常被抛出时,首先搜索抛出该异常的函数。
    • 如果没有找到匹配的 catch 子句
      • 终止该函数,并在调用该函数的函数中继续寻找
      • 如果还是没有找到匹配的 catch 子句,这个新的函数也被终止,继续搜索调用它的函数

以此类推,沿着程序的执行路径逐层回退,直到找到适当类型的 catch 子句为止。

如果最终还是没能找到任何匹配的 catch 子句,程序转到名为 terminate 的标准库函数,执行该函数将导致程序非正常退出

如果一段程序没有 try 语句块且发生了异常,系统会调用 terminate 函数并终止当前程序的执行。

异常中断了程序的正常流程

编写异常安全的代码非常困难,我们必须时刻清除异常何时发生,异常发生后程序应该如何 确保对象有效、资源无泄漏、程序处于合理状态 等等

1.3 标准异常

C++ 标准库定义了一组类,用于报告标准库函数遇到的问题。

4 个头文件:

1.3.1 exception

  • 定义了最通用的异常类 exception
  • 只报告异常的发生,不提供任何额外信息

1.3.2 stdexcept

  • 定义了几种常用的异常类

    定义的异常类
    exception 最常见的问题
    runtime_error 只有在运行时才能检测出的问题
    range_error 运行时错误:生成的结果超出了有意义的值域范围
    overflow_error 运行时错误:计算上溢
    underflow_error 运行时错误:计算下溢
    logic_error 程序逻辑错误
    domain_error 逻辑错误:参数对应的结果值不存在
    invalid_argument 逻辑错误:无效参数
    length_error 逻辑错误:试图创建一个超出该类型最大长度的对象
    out_of_range 逻辑错误:使用一个超出有效范围的值

1.3.3 new

  • 定义了 bad_alloc 异常类型
  • new 申请内存空间如果失败的话抛出这个异常

1.3.4 type_info

  • 定义了 bad_cast 异常类型

1.3.5 初始化

exceptionbad_allocbad_cast 只能默认初始化,也就是无初始值

其他异常类型,应该使用 string 对象或者 C 风格字符串初始化这些类型的对象,创建这类对象时,必须提供初始值

1.3.6 成员函数 what

异常类型之定义了一个名为 what 的成员函数,没有任何参数,返回一个指向 C 风格字符串的 const char*,提供关于异常的一些文本信息。

what 函数返回的 C 风格字符串的内容与异常对象的类型有关。如果异常类型有一个字符串初始值,则 what 返回该字符串。对于其他无初始值的异常类型来说,what 返回的内容由编译器决定。

1.4 练习题

  1. 编写一段程序,从标准输入读取两个整数,输出第一个数除以第二个数的结果

    答案: https://ericdai.xyz/archives/di-5-zhang

  • 思路:

    除数不能为0,这里先不用 try-catch 机制,而是直接输出错误信息

  • 题解:

    #include <iostream>
    using namespace std;
    int main()
    {
        int ival1, ival2;
        cin >> ival1 >> ival2;
        if (ival2 == 0)
        {
            cout << "除数不能为 0" << endl;
            return -1;
        }
        cout << "两数相除的结果是:" << ival1 / ival2 << endl;
        return 0;
    }
    

    这里没有考虑输入的数不是 int 类型的情况,这种情况在 1.5 小节中具体实现

  1. 修改你的程序,使得当第二个数是 0 时抛出异常。先不要设定 catch 子句,运行程序并真地为除数输入零,看看会发生什么?

    • 思路:

      通过抛出异常来提示用户的输入错误

    • 题解:

      #include <iostream>
      #include <stdexcept>
      using namespace std;
      int main()
      {
          cout << "Please enter the dividend and the divisor in order:" << endl;
          int ival1, ival2;
          cin >> ival1 >> ival2;
          if (ival2 == 0)
              throw runtime_error("The divisor cannot be zero!");
          cout << "The quotient of two numbers is: " << ival1 / ival2 << endl;
          return 0;
      }
      

      键入 5 0

      结果:

      5 0
      terminate called after throwing an instance of 'std::runtime_error'
        what():  The divisor cannot be zero!
      
      8 4
      The quotient of two numbers is: 2
      
  2. 修改上一题的程序,使用 try 语句块去捕获异常。catch 子句应该为用户输出一条提示信息,询问其是否输入新数并重新执行 try 语句块的内容

    • 题解:

      #include <iostream>
      #include <stdexcept>
      using namespace std;
      int main()
      {
          cout << "Please enter the dividend and the divisor in order: " << endl;
          int ival1, ival2;
          while (cin >> ival1 >> ival2)
          {
              try
              {
                  if (ival2 == 0)
                      throw runtime_error("ERROR! The divisor cannot be zero!");
                  cout << "The quotient of two numbers is: " << ival1 / ival2 << endl;
                  return 0;
              }
              catch (runtime_error err)
              {
                  cout << err.what()
                      << "\nTry Again? Please enter y or n" << endl;
                  char c;
                  cin >> c;
                  if (!cin || c == 'n')
                      break;
              }
          }
          
          return 0;
      }
      

1.5 C++ 数据输入类型异常处理示例

https://blog.csdn.net/weixin_45652508/article/details/108546384

输入一个整数,判断是否为 素数

  • 如果输入的是小数或者其他字符,返回输入错误的提示

  • 主要用到了 atoi 函数,将字符串转换为 整数,如果不是数字,atoi 会返回 0,以此判断输入的是否是 数字

  • atoi 遇到小数不会报错,所以先查找小数点,排除小数的情况

  • 判断素数用到了 sqrt (square root 平方根) 函数,在 库中

#include <iostream>
#include <cstdlib.h>
#include <cmath>

using namespace std;

bool isPrime(int a)
{
    if (a < 0)
    {
        cout << "a is not a prime number" << endl;
        return false;
    }
    for (int i = 2; i <= int(sqrt(a)); ++i)
    {
        if (a % i == 0)
        {
            cout << "a is not a prime number" << endl;
	        return false;
        }
    }
    cout << "a is a prime number!" << endl;
    return true;
}

int main()
{
    string str;
    int a(1);
    cin >> str;
    try
    {
        // 先搜索小数点
        size_t fi = str.find('.');
        // find 函数在找不到的情况下会返回 npos
        if (fi != str.npos)
            throw 1;	// 有小数点,抛出异常 1
        if (str != "0" && str != "1" && str != "2")
        {
            a = atoi(str.c_str());
            if (a == 0)
                throw 2;	// str 无法转换为 int,抛出异常2
            isPrime(a);
        }
        else 
        {
            if (str == "2") cout << "a is a prime number" << endl;
            else cout << "a is not a prime number" << endl;
        }
    }
    catch (int)
    {
        cout << "The input number is illegal" << endl;
        exit(0);
    }
    return 0;
}

标签:C++,try,error,catch,runtime,异常,throw
From: https://www.cnblogs.com/icewalnut/p/17222435.html

相关文章

  • C++信号量实现线程间同步,windows使用SetEvent,linux使用sem_t,QT测试
     目录windows使用CreateEvent、SetEvent、ResetEvent、WaitForSingleObjectlinux使用sem_init、sem_wait、sem_trywait、sem_post、sem_destroy windows使用C......
  • c++ 创建对象
    1.定义类一般写在.h文件中#include<string>#include<iostream>usingnamespacestd;voidprint(charcontent[]);voidTestString1();voidTestString2();voidTe......
  • C++ string类
    小引C语言中,字符串是以\0结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP(面向对象)的思想,而且底层空间......
  • Dart 异常
    异常抛出异常抛出异常的方式有3种fun()=>throw"error";//胖箭头方法//普通方法fun(){throw"error";}//普通方法fun(){throwException("error");}......
  • [牛客BM70&LeetCode322]零钱兑换Ⅰ——DFS,记忆化搜索,动态规划(C++)
    题目描述给你一个整数数组arr,表示不同面额的硬币;以及一个整数aim,表示需要放入钱包的目标金额。计算并返回可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组......
  • 斯坦福 UE4 C++ ActionRoguelike游戏实例教程 05.认识GameMode&自动生成AI角色
    斯坦福课程UE4C++ActionRoguelike游戏实例教程0.绪论概述本篇文章将会讲述UE中Gamemode的基本概念,并在C++中开发GameMode,为游戏设置一个简单的玩法:使用环境查询自动......
  • 斯坦福 UE4 C++ ActionRoguelike游戏实例教程 04.角色感知组件PawnSensingComponent
    斯坦福课程UE4C++ActionRoguelike游戏实例教程0.绪论概述本文章对应课程第十一章43、44节。本文讲述PawnSensingComponent中的视觉感知的使用,以及对AI角色平滑转身......
  • C++学习记录
    C++recordnotebook基础导论C++特性具有c访问硬件的能力和面向对象程序的属性,以及更具有泛型编程的功能(使用模板进行编程)。OOP(面向对象编程)其中的方法有:自顶向下和......
  • 安装好了calico,机器重启了,k8s异常nfs挂载报错
      节点没有装nfs-utils包吧,安装了依然报错。位于node02上的pod死活起不来,kubectldeletepod--all-nrbd-system把rainbond的pod都删了依然报错。node02上节点到......
  • C++ 构造函数和析构函数
    构造函数和析构函数目录页面问题构造函数与析构函数初始化列表转换构造拷贝构造(这种都是浅拷贝,每一项成员依次拷贝过去)默认的赋值运算符小的总结页面构造/......