目录
一、C++ 异常处理初印象
二、探索 C++ 标准异常类
三、自定义异常类的构建与应用
3.1 自定义异常类的必要性
3.2 自定义异常类的实现步骤
3.3 实际应用场景
四、异常安全保证:守护代码的坚固防线
4.1 异常安全的重要性
4.2 异常安全的三个级别
4.3 实现异常安全的策略与技巧
五、总结与展望
一、C++ 异常处理初印象
在 C++ 编程的世界里,我们常常会遇到各种意外情况,比如除零错误、内存分配失败、数组越界等。这些运行时错误如果不妥善处理,可能会导致程序崩溃,给用户带来糟糕的体验。为了应对这些问题,C++ 引入了异常处理机制。
C++ 的异常处理主要通过try、catch和throw这三个关键字来实现。try块用于包含可能会抛出异常的代码,就像是一个 “安全区”,在这个区域内执行的代码如果遇到问题,就会抛出异常。catch块则用于捕获并处理这些异常,它就像是一个 “异常捕捉器”,专门等待捕获从try块中抛出的异常,并执行相应的处理逻辑。而throw关键字则用于抛出异常,当程序遇到错误情况时,通过throw来通知调用者发生了异常,并传递异常信息。
例如,下面这段简单的代码演示了基本的异常处理:
#include <iostream>int main() {int numerator = 10;int denominator = 0;try {if (denominator == 0) {throw "除数不能为零";}int result = numerator / denominator;std::cout << "结果是: " << result << std::endl;} catch (const char* msg) {std::cerr << "捕获到异常: " << msg << std::endl;}return 0;}
在这个例子中,我们在try块中进行除法运算,如果发现除数为零,就使用throw抛出一个字符串类型的异常。catch块捕获到这个异常,并输出相应的错误信息。这样,即使程序遇到了除零错误,也不会直接崩溃,而是能够优雅地处理这个异常情况。
不过,在实际应用中,仅仅使用基本的异常处理机制是不够的。对于复杂的程序,我们往往需要自定义异常类,以便更精确地处理各种不同类型的异常,同时确保程序的异常安全性。这就是我们接下来要深入探讨的内容。
二、探索 C++ 标准异常类
在深入了解自定义异常类之前,先来熟悉一下 C++ 标准库中已经提供的异常类。C++ 标准库定义了一系列的异常类,它们形成了一个层次结构,std::exception是所有标准异常类的基类。
std::exception类提供了一个虚函数what(),用于返回一个描述异常的字符串。这个基类为所有标准异常提供了一个统一的接口,方便我们在捕获异常时获取异常信息。
从std::exception派生出来的子类有很多,其中一些常见的包括:
-
std::runtime_error:用于表示运行时错误,这类错误在程序运行时才能检测到,例如除零错误、无效的参数值等。它有多个重要的子类,如std::overflow_error(表示算术运算上溢)和std::underflow_error(表示算术运算下溢) 。在进行数值计算时,如果计算结果超出了数据类型所能表示的范围,就可以抛出std::overflow_error或std::underflow_error异常。比如:
#include <iostream>#include <stdexcept>int main() {try {int a = 2147483647; // 这是int类型的最大值int b = 1;int result = a + b; // 这里会发生上溢std::cout << "结果是: " << result << std::endl;} catch (const std::overflow_error& e) {std::cerr << "捕获到上溢异常: " << e.what() << std::endl;}return 0;}
-
std::logic_error:用于表示逻辑错误,这类错误是在程序逻辑设计上的问题,理论上在编译时就可以避免。例如std::invalid_argument(表示无效参数),当函数接收到一个不符合预期的参数时,可以抛出这个异常。比如一个函数要求传入的参数必须是正数,如果传入了负数,就可以抛出std::invalid_argument异常:
#include <iostream>#include <stdexcept>void calculate(int num) {if (num <= 0) {throw std::invalid_argument("参数必须是正数");}// 正常计算逻辑std::cout << "计算结果是: " << num * 2 << std::endl;}int main() {try {calculate(-1);} catch (const std::invalid_argument& e) {std::cerr << "捕获到无效参数异常: " << e.what() << std::endl;}return 0;}
-
std::bad_alloc:当new操作符无法分配足够的内存时,会抛出这个异常。在进行动态内存分配时,比如创建一个大型数组或对象时,如果内存不足,就会触发std::bad_alloc异常:
#include <iostream>#include <stdexcept>int main() {try {int* largeArray = new int[1000000000]; // 尝试分配大量内存,可能会失败} catch (const std::bad_alloc& e) {