在C++中,异常是程序在执行过程中遇到意外情况或错误时抛出的一种机制。当程序运行过程中发生异常,可以通过抛出异常、捕获异常和处理异常来改变程序的正常执行流程。
- 抛出异常:在程序中,可以使用关键字 throw 来抛出异常。当程序遇到错误或异常情况时,可以通过抛出异常来中断当前的执行流程,并尝试寻找异常处理的代码块。
- 捕获异常:在需要处理异常的地方,可以使用 try、catch 块来捕获并处理异常。try 块用于包围可能会抛出异常的代码,而 catch 块则用于捕获并处理异常。当 try 块中的代码抛出异常时,程序的执行流程会跳转到匹配异常类型的 catch 块中进行处理。
- 处理异常:catch 块用于捕获并处理特定类型的异常。当发生异常时,会进行异常的类型匹配,找到匹配的 catch 块,然后执行该块中的处理逻辑。可以在 catch 块中进行异常处理、恢复正常执行或者再次抛出异常。
//感受异常处理
double Fun(int a, int b)
{
if (b == 0)
{
string Exp = "分母为0";
throw Exp;
}
else
{
return (double)a / b;
}
}
int main()
{
while (1)
{
int a = 0;
int b = 0;
cin >> a >> b;
try
{
auto ret = Fun(a, b);
cout << "运算正常:" << ret << endl;
}
catch (const string& Exp)
{
cout << Exp << endl;
}
}
return 0;
}
异常的抛出和匹配原则
- 异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个catch的处理代码
- 被选中的处理代码(catch)是调用链中与该对象类型匹配且离抛出异常位置最近的那一个。
- 抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,所以会生成一个拷贝对象,这个拷贝的临时对象会在被catch以后销毁。(这里的处理类似于函数的传值返回)
- catch(…)可以捕获任意类型的异常,问题是不知道异常错误是什么。
- 实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配,可以抛出的派生类对象,使用基类捕获。
//解释第二条原则
#include <iostream>
#include <stdexcept>
void foo() {
try {
throw std::logic_error("Exception in foo()"); // 抛出异常
}
catch (std::runtime_error& ex) {
std::cout << "Caught runtime_error in foo(): " << ex.what() << std::endl;
}
catch (std::exception& ex) {
std::cout << "Caught exception in foo(): " << ex.what() << std::endl;
}
}
int main() {
try {
foo();
}
catch (std::runtime_error& ex) {
std::cout << "Caught runtime_error in main(): " << ex.what() << std::endl;
}
catch (...) {
std::cout << "Caught unknown exception in main()" << std::endl;
}
return 0;
}
在函数调用链中异常栈展开匹配原则:
1、首先检查throw本身是否在try块内部,如果是再查找匹配的catch语句。如果有匹配的,则调到catch的地方进行处理。 2、没有匹配的catch则退出当前函数栈,继续在调用函数的栈中进行查找匹配的catch。 3、如果到达main函数的栈,依旧没有匹配的,则终止程序。上述这个沿着调用链查找匹配的catch子句的过程称为栈展开。所以实际中我们最后都要加一个catch(…)捕获任意类型的异常,否则当有异常没捕获,程序就会直接终止。 4、找到匹配的catch子句并处理以后,会继续沿着catch子句后面继续执行。
#include <iostream>
class MyException
{
public:
MyException(const string& message)
: m_message(message)
{}
string what() const
{
return m_message;
}
private:
const string m_message;
};
class InvalidDataException : public MyException
{
public:
InvalidDataException(const string& message)
: MyException(message)
{}
};
class DivideByZeroException : public MyException
{
public:
DivideByZeroException(const string& message)
: MyException(message)
{}
};
void processData(int data)
{
if (data < 0)
{
throw InvalidDataException("无效的数据。不允许使用负数值");
}
if (data == 0)
{
throw DivideByZeroException("分母为0");
}
// Process the data...
std::cout << "数据已成功处理" << std::endl;
}
int divide(int dividend, int divisor) {
if (divisor == 0) {
throw DivideByZeroException("分母为0");
}
return dividend / divisor;
}
int main()
{
while (1)
{
try
{
int data;
cin >> data;;
processData(data);
}
catch (const MyException& ex) //InvalidDataException
{
cout << ex.what() << endl;
}
catch (...)
{
cout << "未知异常" << endl;
}
}
return 0;
}
我们自己定义了一个异常父类,使用继承、虚函数重写实现异常处理的多态