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

C++基础系列【36】异常处理

博主介绍:程序喵大人

  • 35- 资深C/C++/Rust/Android/iOS客户端开发
  • 10年大厂工作经验
  • 嵌入式/人工智能/自动驾驶/音视频/游戏开发入门级选手
  • 《C++20高级编程》《C++23高级编程》等多本书籍著译者
  • 更多原创精品文章,首发gzh,见文末
  • 👇👇记得订阅专栏,以防走丢👇👇
    😉C++基础系列专栏
    😃C语言基础系列专栏
    🤣C++大佬养成攻略专栏
    🤓C++训练营
    👉🏻个人网站

异常处理是一项重要的特性,它允许程序在运行时遇到错误条件时能够优雅地恢复或终止执行,而不是简单地崩溃。

C++通过trycatchthrow三个关键字来实现异常处理机制。本文会详细介绍。

基本概念

定义

异常是指在程序执行过程中发生的、不符合程序正常流程的事件。在C++中,异常通常是由于某些错误条件触发的,如除以零、数组越界、内存分配失败等。

关键字

  • try:用于标记可能会抛出异常的代码块,称为保护代码。
  • throw:当检测到异常条件时,使用throw关键字抛出一个异常。throw后面可以跟任意表达式,它的类型决定了抛出的异常类型。
  • catch:用于捕获并处理异常。catch块紧跟在try块之后,并指定了要捕获的异常类型。

用法

下面是一个简单的例子,演示了如何使用trythrowcatch来处理除以零的异常:

#include <iostream>
using namespace std;

double division(int a, int b) {
    if (b == 0) {
        throw "Division by zero!"; // 抛出异常
    }
    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;
}

在这个例子中,如果y为零,division函数将抛出一个字符串异常,该异常在main函数的catch块中被捕获并处理。

一个try块可以跟随多个catch块,来捕获多种不同类型的异常:

#include <iostream>
#include <stdexcept> // 包含标准异常类
using namespace std; // 为了演示方便,项目中不建议这样使用

void testFunction() {
    throw runtime_error("Runtime error occurred!");
}

int main() {
    try {
        testFunction(); // 可能抛出异常的函数
    } catch (const logic_error& e) {
        cout << "Caught a logic_error: " << e.what() << endl;
    } catch (const runtime_error& e) {
        cout << "Caught a runtime_error: " << e.what() << endl;
    } catch (...) { // 捕获所有其他类型的异常
        cout << "Caught an unknown exception" << endl;
    }
    return 0;
}

testFunction抛出了一个runtime_error异常,该异常在第二个catch块中被捕获并处理。最后一个catch块使用省略号...来捕获所有其他类型的异常。

C++标准异常类

C++标准库提供了一系列预定义的异常类,这些类都继承自std::exception基类。

使用标准异常类可以使代码更加清晰。

层次结构

如图

暂时无法在飞书文档外展示此内容

  • std::exception:所有标准异常的基类。
  • std::bad_alloc:内存分配失败时抛出。
  • std::bad_cast:动态类型转换失败时抛出。
  • std::bad_typeid:使用typeid运算符失败时抛出。
  • std::bad_exception:在函数声明中使用了异常规格,但抛出了未列出的异常时抛出(C++11已弃用)。
  • std::logic_error:逻辑错误异常基类,包括:
    • std::domain_error:数学域错误,如sqrt(-1)。
    • std::invalid_argument:无效参数错误。
    • std::length_error:超出允许长度的错误。
    • std::out_of_range:范围错误,如访问vector的非法索引。
  • std::runtime_error:运行时错误异常基类,包括:
    • std::overflow_error:上溢错误。
    • std::range_error:范围错误(与std::out_of_range不同,用于其他情况)。
    • std::underflow_error:下溢错误。

示例

#include <iostream>
#include <stdexcept>
using namespace std;

void testLogicError() {
    throw invalid_argument("Invalid argument error!");
}

void testRuntimeError() {
    throw out_of_range("Out of range error!");
}

int main() {
    try {
        testLogicError(); // 抛出逻辑错误异常
    } catch (const logic_error& e) {
        cout << "Caught a logic_error: " << e.what() << endl;
    }

    try {
        testRuntimeError(); // 抛出运行时错误异常
    } catch (const runtime_error& e) {
        cout << "Caught a runtime_error: " << e.what() << endl;
    }

    return 0;
}

testLogicError函数抛出了一个invalid_argument异常,而testRuntimeError函数抛出了一个out_of_range异常。这两个异常分别在对应的catch块中被捕获并处理。

自定义异常类

虽然C++标准库提供了丰富的异常类,但在某些情况下,开发者可能需要定义自己的异常类。

我们可以通过继承std::exception基类并重载what方法来实现。

示例

#include <iostream>
#include <exception>
#include <string>
using namespace std;

class MyException : public exception {
public:
MyException(const string& message) : message_(message) {}

virtual const char* what() const noexcept override {
    return message_.c_str();
}

private:
string message_;
};

void testCustomException() {
    throw MyException("Custom exception occurred!");
}

int main() {
    try {
        testCustomException(); // 抛出自定义异常
    } catch (const MyException& e) {
        cout << "Caught a MyException: " << e.what() << endl;
    } catch (const exception& e) {
        cout << "Caught an unknown exception: " << e.what() << endl;
    }

    return 0;
}

MyException类继承自std::exception并重载了what方法。testCustomException函数抛出了一个MyException异常,该异常在main函数的catch块中被捕获并处理。

注意,这里还添加了一个捕获所有其他std::exception子类的catch块,确保能够捕获所有未知异常。

noexcept

从C++11开始,推荐使用noexcept关键字来声明函数不抛出任何异常:

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

如果func函数在执行过程中抛出了异常,程序会直接终止。noexcept关键字还可以用于提高性能,因为编译器可以优化不抛出异常的函数调用。

如果你看过gcc源码,你会发现,基本上通篇都是noexcept

noexcept会告诉编译器,它修饰的函数不会产生异常(exception),这有利于编译器做更多的优化。

C++的异常处理是在运行时检测的,而不是在编译时检测,为了运行时检测,编译器应该会做些额外的操作,如果能够通过noexcept明确的告诉编译器这个函数不会抛出异常,编译器应该会做一些优化。

验证函数是否noexcept

noexcept还可以当作运算符,它可以传入参数,来验证某个函数是否是noexcept

void may_throw();
void no_throw() noexcept;
int main() {
    noexcept(may_throw()); // false
    noexcept(no_throw()); // true
    }

什么时候使用noexcept

  • 移动构造函数,移动赋值函数,建议使用noexcept修饰,因为搭配标准库使用时,noexcept作用巨大,它可以优先移动而非拷贝,推荐阅读 move_if_noexcept。
  • 而析构函数默认就是noexcept的,不需要显式指定noexcept
  • 在明确确认某个函数不会产生exception时,可以使用noexcept

推荐看看这几个noexcept相关的文档:

  • https://www.cnblogs.com/sword03/p/10020344.html
  • https://en.cppreference.com/w/cpp/language/noexcept_spec
  • https://en.cppreference.com/w/cpp/language/noexcept
  • https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Re-noexcept
  • https://stackoverflow.com/questions/10787766/when-should-i-really-use-noexcept
  • https://www.heise.de/blog/C-Core-Guidelines-Der-noexcept-Spezifier-und-Operator-4121657.html

异常安全性

异常安全性是指程序在遇到异常时仍然能够保持正确的状态,不会出现资源泄露或者数据不一致等问题。

因为C++允许程序在执行过程中抛出异常,这可能导致程序的控制流发生变化,如果资源管理不当,就可能出现资源泄露或程序崩溃等问题。

码字不易,欢迎大家点赞关注评论,谢谢!


C++训练营

专为校招、社招3年工作经验的同学打造的1V1 C++训练营,量身定制学习计划、每日代码review,简历优化,面试辅导,已帮助多名学员获得大厂offer!

相关文章:

  • 系统设计模块之安全架构设计(身份认证与授权(OAuth2.0、JWT、RBAC/ABAC))
  • 使用WindSurf生成贪吃蛇小游戏:从零开始的开发之旅
  • websoket 学习笔记
  • 【LLM】A2A 与 MCP:剖析 AI Agent 互联时代的两种关键协议
  • 路由引入配置
  • JMeter的高并发和高频率和分布式
  • matplotlib练习
  • Spring Boot 使用 SMB 协议
  • Sentinel源码—1.使用演示和简介二
  • 【算法学习笔记】37:扩展中国剩余定理(EXCRT)求解任意线性同余方程组
  • 【微服务管理】注册中心:分布式系统的基石
  • python每日一练
  • 【模块化拆解与多视角信息3】教育背景:学历通胀时代的生存法则
  • JMeter使用
  • css解决边框四个角有颜色
  • 关于数据清洗和数据处理实践学习笔记
  • 任意文件读取 + java逆向 -- File_download sqctf WP
  • 【中级软件设计师】前趋图 (附软考真题)
  • HJ16 购物单
  • 【Linux生成SSH秘钥实现远程连接】Linux生成SSH秘钥对与修改服务配置文件实现无密码远程连接
  • 三部门:对不裁员少裁员的参保企业实施稳岗返还政策至今年底
  • 南阳市委副书记、政法委书记金浩任内落马
  • 阿塞拜疆总统阿利耶夫将访华
  • 河南社旗县委书记张荣印转任南阳市人大常委会农工委主任
  • 亲诚惠容行大道,命运与共开新篇——中共中央政治局委员、外交部长王毅谈习近平主席对越南、马来西亚、柬埔寨进行国事访问
  • 2025“上海之夏”向全球邀约,首批城市定制活动集中亮相