异常处理是C++中的重要概念之一,用于处理在程序执行过程中可能发生的错误或异常情况。异常是指在程序执行过程中发生的一些不寻常的事件,例如除零错误、访问无效内存等。C++提供了一套异常处理机制,使得程序可以优雅地处理这些异常,提高程序的可靠性和健壮性。

  异常是一种程序控制机制,与函数机制互补。异常处理是一种用于在程序执行过程中处理错误的方法,使得程序能够更加健壮和容错。在 C++ 中,try、catch 和 throw 是用于处理异常的关键字和机制。下面学习一下。

1. try、catch和throw

1.1 try模块

  try: try 关键字用于包围可能会抛出异常的代码块。在 try 块中的代码是被监视的,如果在执行这段代码时发生异常,程序会跳到 catch 块进行异常处理。

  首先,我们需要在可能引发异常的代码块前使用try关键字,将这部分代码封装在一个特殊的块中。

  代码如下:

try {
// 可能引发异常的代码
// ...
} catch (ExceptionType1 e1) {
// 处理 ExceptionType1 类型的异常
} catch (ExceptionType2 e2) {
// 处理 ExceptionType2 类型的异常
} catch (...) {
// 处理其他类型的异常
}

  

1.2 catch模块

  catch: catch 关键字用于定义异常处理块,它跟随在 try 块之后。如果在 try 块中的代码引发了异常,控制流会跳转到匹配异常类型的 catch 块。catch 块中的代码用于处理异常。

  比如下面代码catch处理异常:

try {
// 可能引发异常的代码
// ...
} catch (MyException e) {
// 处理 MyException 类型的异常
std::cerr << "Caught an exception: " << e.what() << std::endl;
} catch (...) {
// 处理其他类型的异常
std::cerr << "Caught an unknown exception" << std::endl;
}

1.3 throw语句

  throw: throw 关键字用于在程序的任何地方抛出异常。当某个条件触发异常时,可以使用 throw 语句来引发异常。通常,throw 语句位于 try 块内。

  当在try块中发现异常情况时,可以使用throw语句抛出一个异常。异常通常是一个对象,可以是任何类型,但通常是派生自std::exception类的对象。

if (/* 发现异常情况 */) {
throw MyException("发现异常情况");
}

  通过结合使用 try、catch 和 throw,可以实现在程序中有效地处理和传递异常,使得程序能够更好地应对各种错误情况。所以说异常处理是 C++ 中一种重要的错误管理机制。

2. 标准异常类 ——std::exception类

  C++标准库提供了一些常用的异常类,它们都是从std::exception类派生而来。标准程序库抛出的所有异常,都派生于该基类。而基类 Exception 定义了一个成员函数 虚函数 what(),它返回一个 C 风格的字符串(const char*),用于描述异常的信息。在实际的异常类中,程序员通常需要重写 what() 函数,以提供有关异常的更具体信息。

  在Exception类中,what() 函数的声明如下:

virtual const char* what() const throw();

  该函数可以在派生类中重定义。

  runtime_error 和 logic_error 是一些具体的异常类的基类,他们分别表示两大类异常。logic_error表示那些可以在程序中被预先检测到的异常,也就是说如果小心地编写程序,这类异常能够避免;而runtime_error则表示那些难以被预先检测的异常。

  一些编程语言规定只能抛掷某个类的派生类(例如Java中允许抛掷的类必须派生自Exception类),C++中虽然没有这项强制的要求,但仍然可以这样实践。例如,在程序中可以使得所有抛出的异常皆派生自Exception,这样会带来很多方便。

  logic_error 和 runtime_error 两个类及其派生类,都有一个接收 const string &型参数的构造函数。在构造异常对象时需要将具体的错误信息传递给该函数,如果调用该对象的what函数,就可以得到构造时提供的错误信息。

2.1  Exception的示例

  以下是 std::exception 的基本结构(注意:std::exception 异常是所有标准 C++ 异常的父类。):

#include <stdexcept>

class exception {
public:
exception() noexcept;
exception(const exception&) noexcept;
exception& operator=(const exception&) noexcept;
virtual ~exception() noexcept;
virtual const char* what() const noexcept;
};

  其中:

  • 构造函数 exception() noexcept:默认构造函数。
  • 复制构造函数 exception(const exception&) noexcept 和赋值运算符 operator=:这两个函数用于异常对象的复制。
  • 虚析构函数 virtual ~exception() noexcept:允许通过基类指针正确销毁派生类对象。
  • 虚函数 virtual const char* what() const noexcept:返回描述异常的字符串,通常由派生类重写以提供更详细的信息。

  以下是一个简单的例子,演示如何自定义一个派生自 std::exception 的异常类:

#include <iostream>
#include <stdexcept> class MyException : public std::exception {
public:
const char* what() const noexcept override {
return "My custom exception occurred";
}
}; int main() {
try {
throw MyException();
} catch (const std::exception& e) {
std::cerr << "Caught an exception: " << e.what() << std::endl;
} return 0;
}

  在这个例子中,MyException 类继承自 std::exception,并重写了 what() 函数,以提供自定义的异常描述信息。在 catch 块中,可以通过基类引用捕获 MyException 类型的异常,而 what() 函数确保返回正确的描述信息。

2.2  其他异常类

  除了 std::exception,C++ 标准库还提供了一些其他常用的异常类,这些类都是直接或间接派生自 std::exception。以下是其中的一些:

  std::runtime_error 是 C++ 标准库中提供的一种异常类,它是 std::exception 类的派生类。std::runtime_error 表示一种运行时错误,通常用于在程序执行期间发生的、无法在编译期检测到的异常情况。这个类的构造函数接受一个字符串参数,用于描述异常的具体信息。

  以下是一些其他与错误相关的标准异常类:

  std::logic_error:表示逻辑错误,通常是由于程序的编程错误引起的,比如在不满足先决条件的情况下调用函数。

throw std::logic_error("Logic error occurred");

  

  std::domain_error:表示域错误,通常是由于参数的值域错误引起的。

throw std::domain_error("Domain error occurred");

  

  std::invalid_argument:表示无效的参数,通常是由于函数参数的值无效引起的。

throw std::invalid_argument("Invalid argument");

  

  std::length_error:表示长度错误,通常是由于对象长度超过其所允许的最大值引起的。

throw std::length_error("Length error occurred");

  

  std::out_of_range:表示超出范围错误,通常是由于访问超出有效范围的对象元素引起的。

throw std::out_of_range("Out of range error");

  这些异常类提供了不同的语义和用途,可以根据具体情况选择合适的异常类来表示异常。当然,你也可以自定义异常类,派生自 std::exception,以便更好地适应你的程序需求。在实际应用中,根据异常的具体性质选择合适的异常类有助于更准确地捕获和处理异常情况。

2.3 自定义异常

  在自定义异常类时,通常建议继承自std::exception,并重写what()函数,以提供异常的描述信息。

#include <iostream>
#include <exception> class MyException : public std::exception {
public:
const char* what() const noexcept override {
return "MyException occurred";
}
}; int main() {
try {
if (/* 某种异常情况 */) {
throw MyException();
}
} catch (std::exception& e) {
std::cerr << "Caught an exception: " << e.what() << std::endl;
} return 0;
}

  您可以通过继承和重载 exception 类来定义新的异常。下面的实例演示了如何使用 std::exception 类来实现自己的异常:

#include <iostream>
#include <exception>
using namespace std; struct MyException : public exception
{
const char * what () const throw ()
{
return "C++ Exception";
}
}; int main()
{
try
{
throw MyException();
}
catch(MyException& e)
{
std::cout << "MyException caught" << std::endl;
std::cout << e.what() << std::endl;
}
catch(std::exception& e)
{
//其他的错误
}
}

  这将产生下面结果:

MyException caught
C++ Exception

  在这里,what() 是异常类提供的一个公共方法,它已被所有子异常类重载。这将返回异常产生的原因。

3. 在实际项目中的应用

3.1 异常处理的实践注意事项

3.1.1 不要滥用异常

  异常处理应该用于处理真正意外的错误,而不是用于控制程序流程。滥用异常会导致代码难以理解和维护。

3.1.2 避免在构造函数和析构函数中抛出异常

  在构造函数和析构函数中抛出异常可能导致资源泄漏或不一致的对象状态,因此应该尽量避免这样做。

3.1.3 使用RAII原则

  资源获取即初始化(RAII)原则是一种通过对象生命周期来管理资源的方法,可以有效减轻异常处理的负担。

3.2  文件读取异常处理

  在实际项目中,异常处理常常用于处理文件操作、网络通信、数据库访问等可能出现异常情况的模块。通过合理使用异常处理,可以提高程序的稳定性,减少因异常导致的不可预知问题。

#include <iostream>
#include <fstream> int main() {
try {
std::ifstream file("example.txt");
if (!file.is_open()) {
throw std::runtime_error("Failed to open file");
} // 读取文件内容并进行处理
// ... } catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl;
} return 0;
}

  以上是一个简单的文件读取例子,通过异常处理来处理文件打开失败的情况,以保证程序在异常情况下也能够有合适的应对措施。

3.3 抛出异常并捕获示例

  您可以使用 throw 语句在代码块中的任何地方抛出异常。throw 语句的操作数可以是任意的表达式,表达式的结果的类型决定了抛出的异常的类型。

  以下是尝试除以零时抛出异常的实例:

#include <iostream>
using namespace std; double division(int a, int b)
{
if( b == 0 )
{
throw "Division by zero condition!";
}
return (a/b);
} int main ()
{
int x = 50;
int y = 0;
double z = 0; try {
z = division(x, y);
cout << z << endl;
}catch (const char* msg) {
cerr << msg << endl;
} return 0;
}

  由于我们抛出了一个类型为 const char* 的异常,因此,当捕获该异常时,我们必须在 catch 块中使用 const char*。当上面的代码被编译和执行时,它会产生下列结果:

Division by zero condition!

  异常处理是C++编程中重要的技能之一,它能够提高程序的可靠性和健壮性。通过理解trycatchthrow的使用,以及合理选择和设计异常类,你可以更好地应对程序中可能出现的各种异常情况。在实际项目中,巧妙地运用异常处理,能够让你的代码更加清晰、可维护,提高整体代码质量。

C++——异常处理模块笔记的更多相关文章

  1. 解析大型.NET ERP系统 设计异常处理模块

    异常处理模块是大型系统必备的一个组件,精心设计的异常处理模块可提高系统的健壮性.下面从我理解的角度,谈谈异常处理的方方面面.我的设计仅仅限定于Windows Forms,供参考. 1 定义异常类型 . ...

  2. NET MVC异常处理模块

    一个简单的ASP.NET MVC异常处理模块   一.前言 异常处理是每个系统必不可少的一个重要部分,它可以让我们的程序在发生错误时友好地提示.记录错误信息,更重要的是不破坏正常的数据和影响系统运行. ...

  3. django-rest-framework-源码解析002-序列化/请求模块/响应模块/异常处理模块/渲染模块/十大接口

    简介 当我们使用django-rest-framework框架时, 项目必定是前后端分离的, 那么前后端进行数据交互时, 常见的数据类型就是xml和json(现在主流的是json), 这里就需要我们d ...

  4. ChCore Lab3 用户进程和异常处理 实验笔记

    本文为上海交大 ipads 研究所陈海波老师等人所著的<现代操作系统:原理与实现>的课程实验(LAB)的学习笔记的第三篇:用户进程与异常处理.所有章节的笔记可在此处查看:chcore | ...

  5. 一个简单的ASP.NET MVC异常处理模块

    一.前言 异常处理是每个系统必不可少的一个重要部分,它可以让我们的程序在发生错误时友好地提示.记录错误信息,更重要的是不破坏正常的数据和影响系统运行.异常处理应该是一个横切点,所谓横切点就是各个部分都 ...

  6. python日志模块笔记

    前言 在应用中记录日志是程序开发的重要一环,也是调试的重要工具.但却很容易让人忽略.之前用flask写的一个服务就因为没有处理好日志的问题导致线上的错误难以察觉,修复错误的定位也很困难.最近恰好有时间 ...

  7. requests 模块笔记

    import requests 请求方式: requests.get("https://www.baidu.com") requests.post("http://htt ...

  8. python之异常处理模块

    一 . python 内置的异常类 在程序运行过程中,如果出现错误,python解释器会创建一个异常对象,并抛出给系统运行时.即程序终止正常执行流程,转而执行异常处理流程. 在某种特殊条件下,代码中也 ...

  9. drf框架的解析模块-异常处理模块-响应模块-序列化模块

    解析模块 为什么要配置解析模块 (1).drf给我们通过了多种解析数据包方式的解析类. (2).我们可以通过配置来控制前台提交的那些格式的数据台解析,那些数据不解析. (3).全局配置就是针对一个视图 ...

  10. 异常处理try-catch-finally笔记

    当程序发生异常时,我们期望:返回到一种安全状态,并能够让用户执行一些其他的命令:或者 允许用户保存所有操作的结果,并以适当的方式终止程序. 异常处理机制:程序的执行过程中如果出现异常,会自动生成一个异 ...

随机推荐

  1. (已解决)pulse secure 连接功能变灰禁用 连接面板找不到

    今天打开 pulse secure 时,发现窗口变成了这样: 连接功能是灰色的,被禁用了: 解决方案: 运行 PulseSecureService 服务. 然后就正常了!

  2. Oracle数据库期末考试--学堂在线

    1.单选题 (2分) 在Oracle数据库中,下面哪类索引最适合SQL范围查找? 2.单选题 (2分) 在创建Oracle数据库表时,下面哪个元素不出现在CREATE TABLE语句中? 3.单选题 ...

  3. Java项目配置Maven依赖时不知需要的最低jdk版本?(报错java: 错误: 无效的目标发行版:17)

    1.问题 在配置SpringBoot项目依赖时,使用了最新的spring-boot-starter-parent 3.1.5,但是出现了java: 错误: 无效的目标发行版:17的报错 2.解决 经过 ...

  4. 使用markdown语法做笔记,相比txt多了很多样式

  5. 如何让你的.NET WebAPI程序支持HTTP3?

    下面我将总结构建Http3的经验,以Token Gateway的项目为例,请注意使用Http3之前你需要知道它的限制, Windows Windows 11 版本 22000 或更高版本/Window ...

  6. [转帖]Grafana连接oracle数据库插件

    Granfana作为前端监控显示程序提供了迅速图形化查看数据库数据的方式.虽然官网提供了部分免费数据库插件,但毕竟太少,最近需要在Oracle数据库上做项目,发现官方的oracle插件是收费的,几经周 ...

  7. 【转帖】nginx变量使用方法详解-3

    https://www.diewufeiyang.com/post/577.html 也有一些内建变量是支持改写的,其中一个例子是 $args. 这个变量在读取时返回当前请求的 URL 参数串(即请求 ...

  8. [转帖]使用Transformers推理

    https://github.com/ymcui/Chinese-LLaMA-Alpaca/wiki/%E4%BD%BF%E7%94%A8Transformers%E6%8E%A8%E7%90%86 ...

  9. Docker 镜像减少体积的思路和方法

    Docker 镜像减少体积的思路和方法 背景 有一个项目感觉镜像有点大 这边同事喊着一起帮忙处理一下. 今天基本上就在客户现场进行处理了. 想着应该把自己想到的东西整理一下. 整体思路 1. 清理do ...

  10. 【转帖】eBay 云计算“网”事:网络超时篇

    https://www.infoq.cn/article/JmCbkA0XX9NqrcX6loIo eBay技术荟 2020-06-19 本文字数:5508 字 阅读完需:约 18 分钟 导读 eBa ...