C++ 异常
一、异常处理
异常处理是一种在程序运行时处理错误和异常情况的机制。它允许程序在遇到问题时不会立即崩溃,而是可以捕获并处理这些异常,从而使程序更加健壮和可靠。
想象你在驾驶一辆汽车,突然遇到一个障碍物。你可以选择立即刹车(捕获异常),或者继续前进(程序崩溃)。显然,刹车是更安全的选择。异常处理就像是汽车的刹车系统,帮助你在遇到问题时安全地停下来。
二、C++中的异常处理机制
C++中的异常处理主要通过三个关键字来实现:try
、catch
和 throw
。
- try:用于标记一个代码块,这个代码块中的代码可能会抛出异常。
- throw:用于抛出一个异常。当程序遇到问题时,可以使用
throw
语句抛出一个异常。 - catch:用于捕获并处理异常。
catch
块中的代码会在try
块中抛出异常时执行。
基本语法
try {// 可能会抛出异常的代码throw exception_object; // 抛出异常
} catch (exception_type1 e1) {// 处理 exception_type1 类型的异常
} catch (exception_type2 e2) {// 处理 exception_type2 类型的异常
} catch (...) {// 处理所有其他类型的异常
}
示例代码
#include <iostream>
#include <stdexcept> // 包含标准异常类double divide(double a, double b) {if (b == 0) {throw std::runtime_error("Division by zero!"); // 抛出异常}return a / b;
}int main() {try {double result = divide(10.0, 0.0); // 可能会抛出异常std::cout << "Result: " << result << std::endl;} catch (const std::runtime_error& e) { // 捕获并处理异常std::cerr << "Error: " << e.what() << std::endl;} catch (...) { // 捕获所有其他类型的异常std::cerr << "An unknown error occurred." << std::endl;}return 0;
}
在这个例子中,divide
函数在除数为零时抛出一个std::runtime_error
异常。main
函数中的try
块调用divide
函数,如果抛出异常,catch
块会捕获并处理这个异常。
在函数调用链中异常栈展开匹配原则
4. 找到匹配的catch子句并处理以后,会继续沿着catch子句后面继续执行。
也就是说当一个异常被抛出时,它会沿着调用栈向上传播,直到找到一个匹配的catch
块。如果在当前函数中没有找到匹配的catch
块,异常会传播到调用该函数的函数,依此类推,直到找到匹配的catch
块或到达main
函数。如果在main
函数中仍未找到匹配的catch
块,程序会终止并调用std::terminate
。当异常被抛出时,C++会进行栈展开,即在传播异常的过程中,会依次退出调用栈中的函数,直到找到匹配的catch
块。在这个过程中,所有局部对象会被销毁,调用它们的析构函数。
#include <iostream>
#include <stdexcept>class Resource {
public:Resource() { std::cout << "Resource acquired" << std::endl; }~Resource() { std::cout << "Resource released" << std::endl; }
};void func3() {throw std::runtime_error("Error in func3");
}void func2() {Resource res;func3();
}void func1() {try {func2();} catch (const std::runtime_error& e) {std::cerr << "Caught exception: " << e.what() << std::endl;}
}int main() {func1();return 0;
}
在这个例子中,func3
抛出一个异常,func2
中的Resource
对象在栈展开过程中被销毁,调用其析构函数。
异常的重新抛出
double Division(int a, int b)
{// 当b == 0时抛出异常if (b == 0){throw "Division by zero condition!";}return (double)a / (double)b;
}
void Func()
{// 这里可以看到如果发生除0错误抛出异常,另外下面的array没有得到释放。// 所以这里捕获异常后并不处理异常,异常还是交给外面处理,这里捕获了再// 重新抛出去。int* array = new int[10];try {int len, time;cin >> len >> time;cout << Division(len, time) << endl;}catch (...){cout << "delete []" << array << endl;delete[] array;throw;}// ...cout << "delete []" << array << endl;delete[] array;
}
int main()
{try{Func();}catch (const char* errmsg){cout << errmsg << endl;}return 0;
}
异常的类型
C++支持多种类型的异常,包括标准库中的异常类(如std::runtime_error
、std::logic_error
等)和用户自定义的异常类。
三、用户自定义异常类:
#include <iostream>
#include <exception>class MyException : public std::exception {
public:const char* what() const noexcept override {return "My custom exception!";}
};int main() {try {throw MyException(); // 抛出自定义异常} catch (const MyException& e) {std::cerr << "Caught exception: " << e.what() << std::endl;} catch (...) {std::cerr << "Caught an unknown exception." << std::endl;}return 0;
}
在这个例子中,我们定义了一个自定义异常类MyException
,并在main
函数中抛出和捕获这个异常。
四、异常安全
五、异常规范
// 这里表示这个函数会抛出A/B/C/D中的某种类型的异常
void fun() throw(A,B,C,D);
// 这里表示这个函数只会抛出bad_alloc的异常
void* operator new (std::size_t size) throw (std::bad_alloc);
// 这里表示这个函数不会抛出异常
void* operator delete (std::size_t size, void* ptr) throw();
// C++11 中新增的noexcept,表示不会抛异常
thread() noexcept;
thread (thread&& x) noexcept;
六、异常的优缺点
优点:
缺点: