当前位置: 首页 > news >正文

C++的 try-catch 结构

文章目录

      • 基本结构
      • 关键要点:
      • 示例讲解
        • 1. 基本异常处理
        • 2. 捕获不同类型的异常
        • 3. 捕获所有类型的异常
        • 4. 自定义异常类
          • 程序执行流程:
        • 5. 抛出和重新抛出异常
      • 例外情况:异常规范(Deprecated)
      • 异常处理的注意事项
      • 总结

C++中的try-catch结构用于异常处理。通过异常处理机制,程序可以在运行过程中捕获和处理错误,而不至于直接导致程序崩溃。try语句块用于标记可能抛出异常的代码,catch语句块用于捕获并处理这些异常。

基本结构

try {// 可能抛出异常的代码
} catch (type1 e1) {// 处理type1类型异常的代码
} catch (type2 e2) {// 处理type2类型异常的代码
} catch (...) {// 处理所有类型异常的代码(可选的,使用"..."表示捕获任意类型异常)
}

关键要点:

  • try 块:包含可能抛出异常的代码。当遇到异常时,程序会跳出try块,进入与异常类型匹配的catch块。
  • catch 块:用于捕获在try块中抛出的异常,执行相应的处理代码。每个catch块处理一种类型的异常。
  • throw 语句:用来抛出异常,通知调用者发生了某种错误或特殊情况。
  • …(省略号):可以用在最后一个catch块,用来捕获任何类型的异常。

示例讲解

1. 基本异常处理
#include <iostream>int main() {try {int a = 10;int b = 0;if (b == 0) {throw "Division by zero error!"; // 抛出异常}std::cout << "Result: " << a / b << std::endl;} catch (const char* e) {  // 捕获异常std::cerr << "Caught an exception: " << e << std::endl;}return 0;
}

输出:

Caught an exception: Division by zero error!

在这个例子中,程序试图进行除法运算,但因为除数b为零,所以通过throw语句抛出了一个异常字符串。在catch块中,异常被捕获并输出错误信息。

2. 捕获不同类型的异常

异常可以是任意类型的对象,如整数、字符串、类对象等。可以为不同类型的异常定义多个catch块:

#include <iostream>int main() {try {int choice = 1;if (choice == 1) {throw 100;  // 抛出整数类型异常} else if (choice == 2) {throw "String exception";  // 抛出字符串类型异常}} catch (int e) {std::cerr << "Caught an integer exception: " << e << std::endl;} catch (const char* e) {std::cerr << "Caught a string exception: " << e << std::endl;}return 0;
}

输出:

Caught an integer exception: 100

在这个例子中,choice1,因此抛出了一个整数类型的异常,被第一个catch块捕获。如果抛出的是字符串异常,则会进入第二个catch块。

3. 捕获所有类型的异常

有时候我们不关心具体的异常类型,只想捕获所有异常。可以使用...(省略号)来捕获所有未明确匹配的异常:

#include <iostream>int main() {try {throw 3.14;  // 抛出double类型异常} catch (...) {std::cerr << "Caught an unknown exception!" << std::endl;}return 0;
}

输出:

Caught an unknown exception!

这里,catch(...)捕获了double类型的异常,尽管没有具体的catch块来处理double,但省略号可以捕获任何类型的异常。

4. 自定义异常类

通常情况下,C++异常处理不仅限于内置数据类型,还可以通过自定义异常类来传递更加详细的错误信息。我们可以定义自己的异常类,并通过throw语句抛出它:

#include <iostream>
#include <string>// 自定义异常类
class MyException {
public:MyException(const std::string& message) : msg_(message) {}const char* what() const noexcept { return msg_.c_str(); }private:std::string msg_;
};int main() {try {throw MyException("Something went wrong!");  // 抛出自定义异常} catch (const MyException& e) {std::cerr << "Caught MyException: " << e.what() << std::endl;}return 0;
}

【代码详解】
以下是逐行讲解和注释:

#include <iostream>  // 引入标准输入输出流库,用于输入输出操作
#include <string>    // 引入字符串库,用于处理 std::string 类型

这两行头文件引入了标准库中的iostreamstringiostream用于输入输出操作,string用于字符串操作。

// 自定义异常类
class MyException {
public:// 构造函数,接收一个字符串参数并将其存储在私有成员变量中MyException(const std::string& message) : msg_(message) {}// what()函数,返回异常信息的C风格字符串(const char*),并使用noexcept确保该函数不会抛出异常const char* what() const noexcept { return msg_.c_str(); }private:std::string msg_;  // 用于存储异常消息的私有成员变量
};
  1. 自定义异常类:创建了一个名为MyException的类,它代表一个自定义的异常类。异常类可以存储异常信息,并提供一个方法返回异常信息。
  2. 构造函数MyException(const std::string& message)是构造函数,它接受一个std::string类型的异常信息,并将其存储在类的私有成员变量msg_中。
  3. what()方法what()方法返回一个const char*(C风格的字符串),用于返回异常信息。该方法标记为noexcept,表示它不会抛出异常。
  4. 私有成员变量msg_:用于存储传入的异常消息。
int main() {

主函数的入口点,程序从这里开始执行。

    try {// 抛出自定义异常,包含消息 "Something went wrong!"throw MyException("Something went wrong!");  } 
  1. try 块:尝试执行可能抛出异常的代码。如果发生异常,程序会跳到对应的catch块。
  2. throw:抛出一个MyException类型的异常对象,异常消息为 "Something went wrong!"。这会立即跳出try块,进入catch块。
    catch (const MyException& e) {// 捕获 MyException 类型的异常,并输出异常信息std::cerr << "Caught MyException: " << e.what() << std::endl;}
  1. catch 块:当抛出MyException类型的异常时,异常会被捕获在catch块中。捕获的是const MyException&,通过引用避免拷贝,同时使用const保护数据不被修改。
  2. 输出异常信息e.what()调用的是自定义异常类中的what()方法,用于获取异常信息。std::cerr用于向标准错误流输出捕获的异常消息。
    return 0;  // 返回0表示程序正常结束
}

程序正常执行完毕后返回0

程序执行流程:
  1. try块中的代码试图抛出一个MyException异常。
  2. 异常被throw语句抛出,并被catch捕获。
  3. catch块输出异常信息,程序继续执行并最终正常退出。

输出:

Caught MyException: Something went wrong!

在这个例子中,我们定义了一个简单的异常类MyException,并在try块中抛出该类型的异常。在catch块中,捕获并输出了该异常的错误信息。

5. 抛出和重新抛出异常

在某些情况下,捕获异常后可能希望将异常继续向上抛出。可以使用throw重新抛出当前捕获的异常。

#include <iostream>void test() {try {throw 20;  // 抛出异常} catch (int e) {std::cout << "Caught in test: " << e << std::endl;throw;  // 重新抛出异常}
}int main() {try {test();} catch (int e) {std::cout << "Caught in main: " << e << std::endl;}return 0;
}

输出:

Caught in test: 20
Caught in main: 20

在这个例子中,test()函数捕获了异常后,使用throw语句将其重新抛出。然后在main()函数中,重新捕获了该异常并处理。

例外情况:异常规范(Deprecated)

在C++98中,函数可以指定它们会抛出哪些异常类型。但自C++11以来,异常规范已被弃用,并引入了新的noexcept关键字,表示函数不会抛出异常。

void foo() noexcept {// 这个函数保证不会抛出任何异常
}

如果在noexcept函数中抛出异常,程序将调用std::terminate直接终止。

异常处理的注意事项

  1. 不要滥用异常:异常处理应该用于无法通过常规手段预测和处理的异常情况。不要使用异常处理来控制正常的程序流,这会降低代码效率并导致混乱。

  2. 性能问题:异常的抛出和捕获是有开销的,尤其是在嵌套的try-catch结构中。因此,在高性能代码中,尽量避免在频繁的操作中使用异常处理。

  3. 捕获异常时使用引用:如果捕获的是类类型异常,最好使用引用(如const MyException& e)来避免不必要的对象拷贝。

  4. 资源释放与异常安全:在处理异常时,特别要注意确保资源(如内存、文件句柄等)能够正确释放,通常通过RAII(资源获取即初始化)模式来保障这一点。

总结

C++中的try-catch结构提供了一种优雅的错误处理机制,允许程序在运行时动态应对异常情况而不至于崩溃。通过自定义异常类、捕获不同类型的异常、甚至捕获所有异常,开发者可以为程序提供更健壮的错误处理。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Gitlab学习(008 gitlab开发工作流GitFlow)
  • 这是一篇给Java小白看的JVM文章
  • Diffusion Models/Score-based Generative Models背后的深度学习原理(7):估计配分函数
  • 【机器学习】——支持向量机
  • antd table 可展开行的多种控制
  • PS教程,从零开始学PS
  • css基础知识笔记
  • 告别枯燥:我开发了一个在电脑桌面上使用弹幕来背单词的软件
  • [数据集][目标检测]中草药类型识别检测数据集VOC+YOLO格式7976张45类别
  • JVM 虚拟机的编译器、类加载过程、类加载器有哪些?
  • 信息技术的革新与未来展望
  • 面试金典题2.6
  • TLV解码 - 华为OD统一考试(E卷)
  • C++第十二节课 模板初阶和string引入
  • 新能源汽车知识点集萃
  • .pyc 想到的一些问题
  • 2017年终总结、随想
  • flutter的key在widget list的作用以及必要性
  • js对象的深浅拷贝
  • PHP面试之三:MySQL数据库
  • Promise面试题,控制异步流程
  • Python 反序列化安全问题(二)
  • python学习笔记-类对象的信息
  • sessionStorage和localStorage
  • 当SetTimeout遇到了字符串
  • 简单实现一个textarea自适应高度
  • 看域名解析域名安全对SEO的影响
  • 坑!为什么View.startAnimation不起作用?
  • 使用parted解决大于2T的磁盘分区
  • 我是如何设计 Upload 上传组件的
  • 项目实战-Api的解决方案
  • 在Mac OS X上安装 Ruby运行环境
  • 怎么把视频里的音乐提取出来
  • 阿里云服务器购买完整流程
  • # 学号 2017-2018-20172309 《程序设计与数据结构》实验三报告
  • #laravel 通过手动安装依赖PHPExcel#
  • $emit传递多个参数_PPC和MIPS指令集下二进制代码中函数参数个数的识别方法
  • (09)Hive——CTE 公共表达式
  • (leetcode学习)236. 二叉树的最近公共祖先
  • (二刷)代码随想录第16天|104.二叉树的最大深度 559.n叉树的最大深度● 111.二叉树的最小深度● 222.完全二叉树的节点个数
  • (翻译)terry crowley: 写给程序员
  • (附源码)spring boot火车票售卖系统 毕业设计 211004
  • (附源码)springboot工单管理系统 毕业设计 964158
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (译)计算距离、方位和更多经纬度之间的点
  • (原創) 如何使用ISO C++讀寫BMP圖檔? (C/C++) (Image Processing)
  • (转)Android中使用ormlite实现持久化(一)--HelloOrmLite
  • .bat批处理(二):%0 %1——给批处理脚本传递参数
  • .NET 5.0正式发布,有什么功能特性(翻译)
  • .Net Core/.Net6/.Net8 ,启动配置/Program.cs 配置
  • .net php 通信,flash与asp/php/asp.net通信的方法
  • .NET 将混合了多个不同平台(Windows Mac Linux)的文件 目录的路径格式化成同一个平台下的路径
  • .NET项目中存在多个web.config文件时的加载顺序
  • /usr/bin/env: node: No such file or directory
  • ::前边啥也没有