C++(三十一)异常处理
c 语言中错误的处理,通常采用返回值的方式或是置位全局变量的方式。这就存在
两个问题。
如果返回值正是我们需要的数据,且返回数据同出错数据容错性不高。
全局变量,在多线程中易引发竞争。
而且,当错误发生时,上级函数要出错处理,层层上报,
造成过多的出错处理代码,且传递的效率低下。
C++ 通过异常实现了返回与错误的处理的分离。
c++异常处理
//todo 异常处理
#include <iostream>
#include <cmath>
using namespace std;
// 计算三角形面积的函数
double triangleArea(double x, double y, double z)
{
double area;
double s = (x + y + z) / 2; // 计算半周长
// 检查是否能构成三角形
if (x + y > z && y + z > x && x + z > y)
area = sqrt(s * (s - x) * (s - y) * (s - z)); // 使用海伦公式计算面积
else
throw -1.0; // 抛出异常,表示不能构成三角形
return area;
}
int main()
{
int a, b, c;
float area;
while (1) // 无限循环,直到手动停止程序
{
try {
cin >> a >> b >> c; // 从标准输入读取三条边的长度
// 检查输入的边长是否为正数
if (a > 0 && b > 0 && c > 0)
{
area = triangleArea(a, b, c); // 调用计算面积的函数
cout << "Area: " << area << endl; // 输出计算得到的面积
}
}
catch (double e) { // 捕获异常
cout << "return " << e << endl; // 输出异常值
cout << "输入的三角形不合法" << endl; // 提示用户输入的三角形不合法
}
}
return 0;
}
分析:
1,把可能发生异常的语句放在 try 语句声当中。try 不影响原有语句的执行流程。
2,若未发生异常,catch 子语句并不起作用。程序会流转到 catch 子句的后面执
行。
3,若 try 块中发生异常,则通过 throw 抛出异常。throw 抛出异常后,程序立
即离开本函数,转到上一级函数。所以 triangleArea 函数中的 return 语句不
会执行。
4,throw 抛出数据,类型不限。既可以是基本数据类型,也可以是构造数据类型。
5,程序流转到 main 函数以后,try 语句块中 抛出进行匹配。匹配成功,执行 catch
语句,catch 语句执行完毕后。继续执行后面的语句。
6,如无匹配,系统调用 terminate 终止程序。
异常定义
try{
被检查可能抛出异常的语句
}
catch(异常信息类型 [变量名]){
进行异常处理的语句
}
使用条例
1,被检语句必须放在 try 块中,否则不起作用。
2,try catch 中花括号不可省。
3,一个 try-catch 结构中,只能有一个 try 块,catch 块却可以有多个。以便
与不同的类型信息匹配。
try{}
catch(double){}
catch(int){}
catch(char){}
catch(float){}
4,throw 抛出的类型,既可以是系统预定义的标准类型也可以是自定义类型。从
抛出到 catch 是一次复制拷贝的过程。如果有自定义类型,要考虑自定义类型的
拷贝问题。
5,异常匹配,不作类型转化。如果 catch 语句没有匹配异常类型信息,就可以用
(...)表示可以捕获任何异常类型的信息。
catch(...)
{
cout<<"catch a unknow exception"<<endl;
}
6.try-catch 结构可以与 throw 在同一个函数中,也可以不在同一个函数中,
throw 抛出异常后,先在本函数内寻找与之匹配的 catch 块,找不到与之匹配的
就转到上一层,如果上一层也没有,则转到更上一层的 catch 块。
如果最终找不到与之匹配的 catch 块,系统则会调有系统函数 terminate 使程序终止。(可能出现打破了,层级管理的设计)
抛出异常声明
1,一个不抛掷任何类型异常的函数可以声明为:
void func() throw(); //推荐使用
2,如果在函数声明中没有包含异常接口声明,则函数可以抛掷任何类型的异常,
例如:
void func(); //不推荐
3,为了加强程序的可读性,可以在函数声明中列出可能抛出的所有异常类型。
例如:
void func() throw (A, B, C , D); //这个函数 func()能够且只能
抛出类型 A B C D 及其子类型的异常。
4,如果一个函数抛出了它的异常接口声明所不允许抛出的异常,该函数默认行为
调用 terminate 函数中止程序。
栈自旋
unwinding
异常被抛出后,从进入 try 块起,到异常被抛掷前,这期间在栈上的构造的所有
对象,都会被自动析构。析构的顺序与构造的顺序相反。这一过程称为栈的解旋
(unwinding)。
#include <iostream>
#include <memory>
using namespace std;
// 定义类A
class A
{
public:
// 构造函数
A()
{
cout << "A constructor" << endl;
}
// 析构函数
~A()
{
cout << "~A destructor" << endl;
}
};
// 定义divide函数,用于执行除法操作
int divide(int x, int y)
{
// 创建一个A类的实例,使用auto_ptr管理其生命周期
auto_ptr<A> pa(new A);
// 如果除数为0,抛出一个字符'a'的异常
if (y == 0)
throw('a');
// 返回除法结果
return x / y;
}
// 定义myDivide函数,用于调用divide函数
int myDivide(int x, int y)
{
// 创建一个A类的实例
A b;
// 调用divide函数
divide(x, y);
}
// 主函数
int main()
{
try
{
// 调用myDivide函数,尝试进行除法操作
myDivide(4, 0);
}
catch (int x)
{
// 捕获整数类型的异常
cout << "x" << endl;
cout << x << endl;
}
catch (double y)
{
// 捕获双精度浮点数类型的异常
cout << "y" << endl;
cout << y << endl;
}
catch (...)
{
// 捕获所有其他类型的异常
cout << "no x, no y" << endl;
}
// 返回0,表示程序正常结束
return 0;
}
异常自定义
#include <iostream>
using namespace std;
// 定义自定义异常类MyException
class MyException
{
public:
// 构造函数
MyException()
{
cout << "MyException constructor" << endl;
}
// 拷贝构造函数
MyException(const MyException &)
{
cout << "MyException copy constructor" << endl;
}
// 析构函数
~MyException()
{
cout << "~MyException destructor" << endl;
}
};
// 定义divide函数,用于执行除法操作
int divide(int x, int y)
{
// 如果除数为0,抛出自定义异常MyException
if (y == 0)
throw(MyException());
// 返回除法结果
return x / y;
}
// 定义myDivide函数,用于调用divide函数
int myDivide(int x, int y)
{
// 调用divide函数
divide(x, y);
}
// 主函数
int main()
{
try
{
// 调用myDivide函数,尝试进行除法操作
myDivide(4, 0);
}
catch (const MyException &a) // 捕获自定义异常MyException的引用
{
// 捕获到异常后输出信息
cout << "myexception" << endl;
}
// 返回0,表示程序正常结束
return 0;
}
输出
MyException constructor
myexception
~MyException destructor
标准异常
#include <iostream>
using namespace std;
int main()
{
// double *p;
// for(int i=0; i<1000; i++)
// {
// p = new double[900000];
// }
try{
double *p;
for(int i=0; i<1000; i++)
{
p = new double[900000];
}
}catch(std::bad_alloc e){
cout<<e.what()<<endl;
exit(-1);
}
return 0;
}
输出: std::bad_alloc
在C++中,标准库提供了一系列的标准异常类,这些异常类都继承自std::exception。以下是一些常见的标准异常及其含义:
std::exception:这是所有标准C++异常的基类。
std::logic_error:表示程序逻辑错误,通常是由于程序员的错误导致的。
std::invalid_argument:表示传递了无效的参数。
std::domain_error:表示数学上的定义域错误。
std::length_error:表示试图创建一个超出其最大允许大小的对象。
std::out_of_range:表示访问超出范围的元素。
std::runtime_error:表示在运行时才能检测到的错误,通常是由于环境因素导致的。
std::range_error:表示计算结果超出了允许的范围。
std::overflow_error:表示算术运算上溢。
std::underflow_error:表示算术运算下溢。
std::bad_alloc:表示内存分配失败。
std::bad_cast:表示动态类型转换失败。
std::bad_typeid:表示在使用typeid操作符时,操作数是一个空指针。
std::bad_exception:表示处理意外异常失败。
这些异常类通常用于在程序中捕获和处理异常情况,以提高程序的健壮性和可靠性。