IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++之异常处理 -> 正文阅读

[C++知识库]C++之异常处理

程序的错误大致可以分为三种,分别是语法错误、逻辑错误和运行时错误:
1.语法错误在编译和链接阶段就能发现,只有 100% 符合语法规则的代码才能生成可执行程序。语法错误是最容易发现、最容易定位、最容易排除的错误,程序员最不需要担心的就是这种错误。
2.逻辑错误是说我们编写的代码思路有问题,不能够达到最终的目标,这种错误可以通过调试来解决。
3.运行时错误是指程序在运行期间发生的错误,例如除数为 0、内存分配失败、数组越界、文件不存在等。C++ 异常(Exception)机制就是为解决运行时错误而引入的。
4.运行时错误如果放任不管,系统就会执行默认的操作,终止程序运行,也就是我们常说的程序崩溃(Crash)。C++ 提供了异常(Exception)机制,让我们能够捕获运行时错误,给程序一次“起死回生”的机会,或者至少告诉用户发生了什么再终止程序。

没捕获异常的程序

#include <iostream>
#include <string>

using namespace std;

int main() {
    string str = "http://www.baidu.com";
    char ch1 = str[100];
    cout << ch1 << endl;
    char ch2 = str.at(100);
    cout << ch2 << endl;
    return 0;
}
输出:
//第一次输出的是空行,这是因为ch1数组越界了,但是不会报异常
//下面这个异常是at()抛出的
terminate called after throwing an instance of 'std::out_of_range'
  what():  basic_string::at: __n (which is 100) >= this->size() (which is 20)

at() 是 string 类的一个成员函数,它会根据下标来返回字符串的一个字符。与[ ]不同,at() 会检查下标是否越界,如果越界就抛出一个异常;而[ ]不做检查,不管下标是多少都会照常访问。

捕获异常

C++中使用try{}catch(){}来捕获异常

#include <iostream>
#include <string>
#include <exception>

using namespace std;

int main() {
    string str = "http://www.baidu.com";
    try {
        char ch1 = str[100];
        cout << ch1 << endl;
    } catch (exception e) {
        cout << "[1]out of bound" << endl;
    }
    try {
        char ch2 = str.at(100);
        cout << ch2 << endl;
    } catch (exception &e) {
        cout << "[2]out of bound!" << endl;
    }
    return 0;
}
输出:

[2]out of bound!

第一个 try 没有捕获到异常,输出了一个没有意义的字符(垃圾值)。因为[ ]不会检查下标越界,不会抛出异常,所以即使有错误,try 也检测不到。换句话说,发生异常时必须将异常明确地抛出,try 才能检测到;如果不抛出来,即使有异常 try 也检测不到。所谓抛出异常,就是明确地告诉程序发生了什么错误。
第二个 try 检测到了异常,并交给 catch 处理,执行 catch 中的语句。需要说明的是,异常一旦抛出,会立刻被 try 检测到,并且不会再执行异常点(异常发生位置)后面的语句。本例中抛出异常的位置是at() 函数,它后面的 cout 语句就不会再被执行,所以看不到它的输出。
说得直接一点,检测到异常后程序的执行流会发生跳转,从异常点跳转到 catch 所在的位置,位于异常点之后的、并且在当前 try 块内的语句就都不会再执行了;即使 catch 语句成功地处理了错误,程序的执行流也不会再回退到异常点,所以这些语句永远都没有执行的机会了。

抛出异常

C++中throw关键字用来抛出一个异常,这个异常会被 try 检测到,进而被 catch 捕获。

#include <iostream>
#include <string>
#include <exception>

using namespace std;

const char *ERRORLOG = "Unknown Exception";

void func_error() {
    throw ERRORLOG;
    cout << "this code will not be executed" << endl;
}

int main() {
    try {
        func_error();
        cout << "this code will not be executed too" << endl;
    } catch (const char *&e) {
        cout << e << endl;
    }
    return 0;
}
输出:
Unknown Exception


func_error() 在 try 块中被调用,它抛出的异常会被 try 检测到,进而被 catch 捕获。从运行结果可以看出,func_error() 中的 cout 和 try 中的 cout 都没有被执行。
再来看一个例子:

#include <iostream>
#include <string>
#include <exception>

using namespace std;

const char *ERRORLOG = "Unknown Exception";

void func_error_inner() {
    throw ERRORLOG;
    cout << "this code will not be executed" << endl;
}

void func_error_outer() {
    func_error_inner();
    cout << "this code will not be executed!" << endl;
}

int main() {
    try {
        func_error_outer();
        cout << "this code will not be executed too" << endl;
    } catch (const char *&e) {
        cout << e << endl;
    }
    cout << "the statement outside of the try_catch is called normal" << endl;
    return 0;
}
输出:
Unknown Exception
the statement outside of the try_catch is called normal

发生异常后,程序的执行流会沿着函数的调用链往前回退,直到遇见 try 才停止。在这个回退过程中,调用链中剩下的代码(所有函数中未被执行的代码)都会被跳过,没有执行的机会了。本例中try检测到func_error_outer()中有异常,回退中发现异常抛出的位置发生在func_error_inner(),在此函数体中捕获到异常停止执行之后的代码。

在使用try_catch中发现,catch关键字后面的括号里有异常类型的形参,其中exception是异常类型,e是一个形参变量,用来接收异常信息。当程序抛出异常时,会创建一份数据,这个数据中包含了错误信息,在开发的时候可以根据这些信息来判断程序到底出了什么问题。
异常既然是一份数据,那么就应该有数据类型。C++ 规定,异常类型可以是 int、char、float、bool 等基本类型,也可以是指针、数组、字符串、结构体、类等聚合类型。C++ 语言本身以及标准库中的函数抛出的异常,都是 exception 类或其子类的异常。也就是说,抛出异常时,会创建一个 exception 类或其子类的对象。
异常类型的形参和函数的形参非常类似,当异常发生后,会将异常数据传递给e这个变量,这和函数传参的过程类似。当然,只有跟 exception类型匹配的异常数据才会被传递给 e,否则 catch 不会接收这份异常数据,也不会执行 catch 块中的语句。换句话说,catch 不会处理当前的异常。
但是 catch 和真正的函数调用又有区别:
1.真正的函数调用,形参和实参的类型必须要匹配,或者可以自动转换,否则在编译阶段就报错了。
2.而对于 catch,异常是在运行阶段产生的,它可以是任何类型,没法提前预测,所以不能在编译阶段判断类型是否正确,只能等到程序运行后,真的抛出异常了,再将异常类型和 catch 能处理的类型进行匹配,匹配成功的话就“调用”当前的 catch,否则就忽略当前的 catch。
总起来说,catch 和真正的函数调用相比,多了一个在运行阶段将实参和形参匹配的过程。
另外需要注意的是,如果不希望 catch 处理异常数据,也可以将 e省略掉,写作:

try{
//可能抛出异常
}catch(exception){
//处理异常
}

这样只会将异常类型和 catch 所能处理的类型进行匹配,不会传递异常数据了。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-10 13:14:33  更:2021-08-10 13:16:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 19:18:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码