首页 > 编程语言 >C++面向对象整理(10)之异常与捕获(try、catch、throw、noexcept)

C++面向对象整理(10)之异常与捕获(try、catch、throw、noexcept)

时间:2024-03-27 13:00:14浏览次数:35  
标签:std 10 抛出 noexcept throw catch 异常 捕获

C++面向对象整理(10)之异常与捕获(try、catch、throw、noexcept)

注:整理一些突然学到的C++知识,随时mark一下
例如:忘记的关键字用法,新关键字,新数据结构


C++ 的 异常的捕获


提示:本文为 C++ 中 异常、try、throw、catch 的写法和举例


一、异常与捕获

1、C++异常处理关键字:try, throw, catch

throw:用于在代码块中抛出异常。格式是throw 变量或对象名字,可以是匿名对象即类名(),可以出现异常的代码放到try块中,然后利用throw抛出异常。
try:用于包围可能出现异常的代码块。格式是try {}
catch:用于捕获并处理异常。格式是catch (类型) {},会根据捕获到的数据类型写多个catch语句。catch与try配合出现。

将可能抛出异常的代码放入try块中,这样当异常发生时,程序不会立即崩溃,而是会跳转到与之关联的catch块进行处理。一旦异常被捕获并处理完毕后,控制流将离开catch块,并继续执行try-catch结构之后的代码。

当检测到某个错误或异常情况时,可以使用throw关键字抛出一个异常。这可以是内置的数据类型(如int,char等),也可以是自定义的类类型。

示例:

throw "An error occurred"; // 抛出字符串字面量  
throw 0; // 抛出整数值  
throw MyException(); // 抛出自定义异常对象
//利用catch捕获异常

catch块用于捕获并处理异常。你可以根据抛出的异常类型来编写不同的catch块。

示例:

try {  
    // ... 可能会抛出异常的代码,里面会有throw语句 ...  
} catch (const char* msg) {  
    // 处理字符串类型的异常  
} catch (int num) {  
    // 处理整数类型的异常  
} catch (const MyException& e) {  
    // 处理自定义异常  
}
//catch(类型) 如果想捕获其他类型再用 
  catch(...){
  //处理其他的
  }

当你不确定可能会抛出什么类型的异常,或者想要捕获所有类型的异常时,可以只使用catch(…)。这是一个捕获所有异常的通用catch块。

在catch块中,如果你不想处理异常,而是想将其继续向上抛出,可以在catch块中再次使用throw关键字(没有参数),这将重新抛出最近捕获的异常。

示例:

catch (const std::exception& e) {  
    std::cerr << "Caught an exception: " << e.what() << std::endl;  
    throw; // 重新抛出异常  
}

异常必须有代码进行处理,不去处理话程序自动调用terminate函数触发中断。如果异常没有被任何catch块捕获,程序将调用std::terminate函数,这通常会导致程序终止。

2、try、catch、throw配合使用的例子

以下是一个不包含自定义异常类的简单例子,仅使用C++标准库中的异常类型,并展示了try、catch和throw的用法:

#include <iostream>  
#include <stdexcept> // 包含标准异常类  
  
// 一个函数,当条件不满足时抛出异常  
void checkCondition(bool condition) {  
    if (!condition) {  
        throw std::runtime_error("Condition is not met!"); // 抛出运行时异常  
    }  
    std::cout << "Condition is met, no exception thrown." << std::endl;  
}  
  
int main() {  
    try {  
        // 调用函数并传入一个不满足条件的值  
        checkCondition(false);  
          
        // 如果checkCondition没有抛出异常,这里的代码将会执行  
        std::cout << "Continuing after checkCondition..." << std::endl;  
          
    } catch (const std::runtime_error& e) {  
        // 捕获运行时异常并处理  
        std::cerr << "Caught an exception: " << e.what() << std::endl;  
        // 在这里可以执行一些错误处理逻辑  
    } catch (...) {  
        // 捕获所有其他类型的异常  
        std::cerr << "Caught an unknown exception!" << std::endl;  
        // 这里处理未知异常的通用逻辑  
    }  
      
    // 无论是否抛出异常,都会执行到这里  
    std::cout << "Program continues after exception handling (if any)." << std::endl;  
      
    return 0;  
}

在这个例子中:

checkCondition函数检查一个条件(在这里是一个布尔值)。如果条件不满足(即condition为false),函数会抛出一个std::runtime_error异常。在main函数中,我们尝试调用checkCondition(false),这会导致异常被抛出。

第一个catch块捕获std::runtime_error类型的异常,并打印出异常信息。第二个catch块是一个通用捕获块,它可以捕获所有其他类型的异常(虽然在这个简单的例子中可能不会被用到)。
如果checkCondition函数中的条件被满足(即传入true),则不会抛出异常,程序将输出“Condition is met, no exception thrown.”并继续执行后续代码。

3、自定义数据类型的异常抛出

异常可以是自定义数据类型,可以创建自己的类来表示特定的异常,并在throw语句中抛出这些类的实例。这

一般使用标准库中的exception类,继承他并重写what函数,示例:

class MyException : public std::exception {  
public:  
    const char* what() const noexcept override {  
        return "My custom exception occurred";  
    }  
};  
 
throw MyException(); // 抛出自定义异常

如果想要创建自定义的异常类并重写what函数,需要从标准异常类(如std::exception)继承,并覆盖what成员函数。what函数是一个返回const char*类型的成员函数,它应该返回描述异常原因的字符串

下面是一个简单的例子,展示了如何创建一个自定义异常类并重写what函数:

#include <iostream>  
#include <string>  
#include <exception> // 包含std::exception  
  
// 自定义异常类  
class MyException : public std::exception {  
private:  
    std::string message; // 用于存储异常信息的字符串  
  
public:  
    // 构造函数,接受一个字符串作为异常信息  
    explicit MyException(const std::string& msg) : message(msg) {}  
  
    // 重写what函数,返回异常信息  
    const char* what() const noexcept override {  
        return message.c_str();  
    }  
};  
  
int main() {  
    try {  
        // 抛出自定义异常  
        throw MyException("This is a custom exception!");  
    } catch (const MyException& e) {  
        // 捕获自定义异常并处理  
        std::cerr << "Caught an exception: " << e.what() << std::endl;  
    } catch (const std::exception& e) {  
        // 捕获所有标准异常  
        std::cerr << "Caught a standard exception: " << e.what() << std::endl;  
    } catch (...) {  
        // 捕获所有其他类型的异常  
        std::cerr << "Caught an unknown exception!" << std::endl;  
    }  
      
    return 0;  
}

在这个例子中:

MyException类从std::exception继承。
它有一个私有成员变量message,用于存储异常信息。
构造函数接受一个字符串参数,并将其赋值给message。

what函数被重写以返回message的C风格字符串表示(通过调用std::string::c_str())。注意,what函数的返回类型是const char*,因此我们不能直接返回std::string对象。相反,我们返回std::string对象内部字符数组的指针,这可以通过调用c_str()成员函数来实现。

在main函数中,我们抛出一个MyException实例,并使用catch块捕获它。捕获到异常后,我们调用e.what()来获取并打印异常信息。

此外,what函数的声明中包含了noexcept关键字,这表示该函数不会抛出任何异常。这是因为在异常处理过程中,如果what函数本身又抛出了异常,那么程序的行为将是未定义的。因此,当你重写what函数时,也应该确保其是noexcept的。

4、值捕获、引用捕获、指针捕获

我们更倾向于使用值或引用捕获异常,因为它们更简单、更安全,并且避免了手动内存管理的需要。只有在特定情况下,例如当异常对象非常大且不可能被复制时,才应考虑使用动态分配和指针捕获。但即使在这种情况下,也应确保正确地管理内存。

推荐的做法是直接抛出异常对象,并使用引用捕获来避免不必要的复制。避免抛出指针或动态分配的对象,除非有明确的理由和适当的内存管理策略。

 throw MyException(); 
 catch (MyException &e){ //...}

使用引用捕获(catch (MyException &e))是推荐的做法,因为它避免了不必要的复制。异常对象直接传递给catch块中的引用,不会调用拷贝构造函数。因此,它更高效,特别是当异常对象较大或包含资源时。

5、noexcept

在C++中,如果你想限定抛出异常的类型,你可以使用异常规格(exception specifications)。然而,如果你需要指定函数可能抛出的异常类型,可以使用异常规格列表。从C++17开始,这种旧式的异常规格列表已经被废弃,推荐使用noexcept关键字来表明一个函数不会抛出任何异常。

在C++17之前的版本中,你可以这样声明一个函数的异常规格列表来指定类型

void myFunction() throw(MyException); // 指定只能抛出MyException类型的异常

上面的代码表示myFunction函数只能抛出MyException类型的异常。如果函数试图抛出其他类型的异常,编译器会报错。然而,这种异常规格方式在C++11及以后的版本中被认为是不够灵活的,并且在C++17中已被弃用。

推荐使用noexcept关键字来指定函数不会抛出异常:

void myFunction() noexcept; // 声明函数不会抛出任何异常

如果函数确实可能抛出异常,并且你希望不限制异常类型,则不需要使用任何异常规格。如果你确实需要声明可能抛出的异常类型,建议使用文档或注释来说明这一点,而不是使用已弃用的异常规格语法。

如果你确实想要通过接口(即基类)来限定派生类能抛出的异常类型,你可以使用纯虚函数和异常规格结合的方式,但这种方式比较复杂,且由于旧式异常规格已被弃用,不推荐这样做。

总结

标签:std,10,抛出,noexcept,throw,catch,异常,捕获
From: https://blog.csdn.net/ULTRAmanTAROACE/article/details/137072356

相关文章

  • P10185的题解
    (一)考虑对每一种颜色单独求解。对于一次第\(k\)种的“循环”,美丽度会加上\[\sum_{i=1}^{a_k}C_{n}^{i}\timesv_k^{i}=(v_k+1)^{a_k}-1\]相信大家都学过二项式定理。“循环”次数取决于其他珠子是否出现,即\(2^{\sum_{i=1}^{a_i}-a_k}\)。再将两式相乘就愉快AC了。(二)警......
  • 《引流108招》第1招:挖墙脚战术
    有好项目却找不到意向客户,只能看着别人赚钱?平台引流总是违规,账号老被封?《引流108招》:108个最新最有效的,0基础小白都能学会的引流获客秘籍,帮你掌握流量密码,实现简单粗暴每日引流100+,疯狂收钱你好,我是独立开发者小黑今天我们跟大家讲一招微信体系内的引流玩法,我给它取名为"......
  • 【揭秘】企讯通106短信通知:那些你不可不知的“幕后功臣”
    在日常生活中,你是否留意过那些来自“106”开头的短信?它们悄无声息地出现在你的手机收件箱,为你带来账单提醒、验证码、优惠活动等各类重要信息。看似平淡无奇的“106短信通知”,实则蕴含着丰富的科技力量与智慧结晶。今天,就让我们一起揭开它的神秘面纱,探索那些让你生活更便捷、信......
  • E810-CQDA1 E810-CQDA2 E810-2CQDA2 E810-XXVDA2 E810-XXVDA4 英特尔®以太网800系列
    E810-CQDA1E810-CQDA2E810-2CQDA2E810-XXVDA2E810-XXVDA4英特尔®以太网800系列Linux性能调优指南(第二篇续)4.0性能故障处理4.1CPU利用率在工作负载运行时检查每个核心的CPU利用率。注意:与总体CPU利用率相比,每个核心的利用率与性能更相关,因为它提供了每个网络队列......
  • Communications link failureThe last packet successfully received from the server
    出现这种错误的大致情况如下:网络问题:可能存在网络中断、网络延迟或者网络拥塞等问题,导致应用程序无法与数据库建立稳定的连接。可以通过检查网络连接是否稳定来解决这个问题。数据库服务器问题:数据库服务器可能出现了问题,例如数据库服务未启动、数据库服务器资源不足、数......
  • Proteus8.0仿真应用设计(十七)基于FreeRTOS、STM32F103C8、HAL库、DHT11、LCD12864的温
    一、简介:        DHT11是一款湿、温度一体化的数字传感器。该传感器包括一个电阻式测湿元件和一个NTC测温元件。DHT11与单片机之间能采用简单的单总线进行通信,仅仅需要一个I/O口。通过单片机等微处理器简单的电路连接就能够实时的采集本地湿度和温度。传感器内部......
  • 全网最简单最快捷的搭建nextcloud教程(开箱即用),也可以说是保姆级虚拟机安装Ubuntu23.
    nextcloud是一款开源的网盘工具,适用于个人或中小型公司。纯英文的官网很多同行看着云里雾里的,网上的教程也零零散散的,容易踩坑。今天我来发一个最简单最快捷的搭建nextcloud的教程。完全傻瓜化,非docker方式。本质上就是Ubuntu23.10自带nextcloud包,安装最后一步的时候勾选上即......
  • win10开机桌面无限刷新闪屏怎么办?win10开机桌面无限刷新闪屏全方位解决方法分享
    有的win10用户在电脑开机之后遇到了桌面无限刷新闪屏的情况,导致无法正常操作,那么应该这么解决呢?下面一起来看看吧!系统配置还原默认设置按Win+R键打开运行对话框,输入msconfig并按回车键打开系统配置窗口。在“常规”选项卡下,选择“正常启动”(这将禁用非核心启动项)。转到......
  • 最详细爬虫零基础教程10——json格式提取之jsonpath
    文章目录一、json数据解析二、案例演示1.解析获得数据2.简化代码3.豆瓣json数据解析总结一、json数据解析用来解析多层嵌套的json数据;JsonPath是一种信息抽取类库,是从JSON文档中抽取指定信息的工具,提供多种语言实现版本,包括:Javascript,Python,PHP和Java。语......
  • 代码随想录算法训练营第十七天|110.平衡二叉树、257.二叉树的所有路径、404.左叶子之
    文档链接:https://programmercarl.com/LeetCode110.平衡二叉树题目链接:https://leetcode.cn/problems/balanced-binary-tree/思路:这里强调一波概念:二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数。二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数......