异常处理的基本概念
-
异常处理机制:暂缓问题处理,不在当前函数中处理,在它的调用者中处理(类似先上车,后补票)
-
什么是异常:任何东西都可以认为是异常,错误只是异常的一种 - - - 出现一种情况让程序不能正常运行,这种情况我们都可以把它当做异常
-
异常一旦被抛出,不做处理,如果引发异常,会调用默认abort函数终止程序
-
捕获和处理异常
-
throw 抛出异常: (可以理解为返回一个值,值是任何类型都可以,是我们处理异常的一个参照)
-
try(检查,捕获异常)和catch(处理异常): 必须是一起出现,并且它们的括号{ }不能省略
-
什么是异常?怎么抛出?
//求a和b的余数 怎么抛出异常
int division(int a,int b)
{
if(b==0)
throw 0; //抛出一个值(任意)--->之后处理
return a/b;
}
void print(int a,int b)
{
cout << division(a,b);
}
int main()
{
print(1,0); //引发异常不做处理
}
/*除数不能为0 把b==0 的情况称为异常 b==0 时代码不成立会中断当前程序*/
异常一旦被抛出,不做处理,如果引发异常,会调用默认abort函数终止程序(引发了标准库中的异常)
捕获和处理异常
//try 与catch必须是一起出现的,并且他们各自的括号{}不能省略
try
{
//正常需要检查是否存在异常的代码
}
catch(类型) //理解为switch中case语句 如果存在异常会跳到catch的位置
{
//处理是根据抛出数据类型决定如何处理 匹配项:匹配抛出异常的类型
}
//一个try可以对应多个catch
try
{
//...
}
catch(int)
{
}
catch(double)
{
}
catch(string)
{
}
//catch和if else_if 执行机制一样,只能执行一个匹配项
小知识:
-
对try{ } catch(){ } 的理解:在当前位置引发异常,直接从这个位置跳到catch的位置执行catch的代码 - - - 类似 switch case 语句
-
catch中int和char不会做类型转换
-
抛出两个相同的类型异常不被允许,报错:"int":由"int"在第 n 行上被捕获
int division(int a,int b)
{
if(b==0)
throw 0;
return a/b;
}
void print(int a,int b)
{
cout<<division(a,b);
}
int main()
{
try //检查异常
{
print(1,0);
cout<<"别的代码"<<endl; //会直接跳到catch,这一句不会运行
}
catch(int) //抛出的是int类型,捕获int类型
{
cout<<"除数不能为0"<<endl;
}
}
/*输出*/
除数不能为0
程序中能存在多个异常,但是只能引发1个异常,不可能同时引发多个异常
不存在异常的描述 - - - 标识性作用
-
throw( )
-
noexcept
//某个函数不存在异常,在某个函数后面用throw() 描述表示它不存在异常
void print() throw()
{
cout << "当前函数不存在抛出异常操作" << endl;
}
void printData() noexcept
{
cout << "c++新标准中的关键字: 不存在抛出异常" << endl;
//throw 0; 报错:一旦说明没有异常就不能写抛出操作
}
删减符 . . .
任何类型的异常都捕获 不管抛出啥,在哪里抛出的,只要引发异常都可以捕获到
catch(...)
{
cout <<"捕获任何类型的异常"<< endl;
}
异常处理中的传参操作 - - - 可以写一个变量进去
-
catch(int a)
-
隐藏了一个传参操作 可以传任何类型,包括自定义类型都可以
-
想要处理抛出字符串的异常处理,注意string类型与const char* 类型区别
抛出字符串,隐藏了一个传参操作
int divisor(int a, int b)
{
if (b == 0)
throw "除数不能为0"; //解析为char* 类型 catch中不能当作string处理
if(b == 1)
throw "除数不能为1"; /*不同问题的抛出,不能用固定类型(int、char...), 可以选择
抛出不同字符串处理 string1,string2,string3...(通过传参的方式去描述问题) */
if(b == 2)
throw "除数不能为2";
return a / b;
}
int main()
{
try
{
divisor(1, 0);
}
catch (string str) //把throw的内容赋值给str str = "除数不能为0"
{
cout << str << endl;
}
}
//调用abort函数终止程序
//改正:
catch (const char* str)
{
cout << str << endl;
}
/*输出*/
除数不能为0
函数中隐藏的传参操作时注意string 和const char* 的处理 - - - 出现类型不匹配的问题,c++对传参类型要求更严格,想要捕获string类型的异常,需要构造无名参数作捕获处理
int divisor(int a, int b)
{
if(b==2)
throw string("除数不能为2"); //捕获对象需要自己触发
return a / b;
}
int main()
{
try
{
divisor(1, 2);
}
catch (string str) //如果想捕获string类型,需要自己构造一个string对象返回
{
cout << str << endl;
}
}
/*输出*/
除数不能为2
可以抛出自己类的对象 - - - 传自定义类型
定义一个错误类Error,程序中所有错误都归到Error类中 - - - 在做数组插入 & 删除时要考虑里面是否为空或者是满的状态 把数组满了的情况称为一种异常
class Error
{
public:
Error(const char* str = "未知错误") :str(str) {} //构造函数
const char* what()const
{
return str.c_str(); //成员函数
}
protected:
string str;
};
//实现数组的插入,下标可能存在问题(数组下标溢出) 数组最大下标!=个数
//插入的数组 当前数组长度(元素个数) 插入的元素 最大数组长度(最大长度为3|最大下标为2)
void insertArray(int array[], int* curNum, int posData,int maxLength)
{
if (*curNum >= maxLength) //3>=3 注意3>3的情况,还是会做插入操作
{
throw Error("数组下标溢出!"); //当前数组满了当作是异常抛出
}
//0 1 2
array[*curNum] = posData; //array[3]=3 下标没问题把元素插到数组中即可
(*curNum)++; //当前元素长度增加
}
int main()
{
try
{
int array[3] = { 0,0,0 };
int curNum = 0;
for (int i = 0; i < 4; i++)
{
insertArray(array, &curNum, i, 3);
}
}
catch (Error str) //抛出自己的对象
{
cout << str.what() << endl;
}
return 0;
}
标准库当中的异常类
#include<exception> //父类(基类)
子类很多,子类描述的问题不同而已
例: const char* _ptr; 用于描述标准库当中异常的字符串,用字符指针存放那个字符串
what( )方法 用于返回数据成员的 虚函数 & 不存在异常
return _ptr ? _ptr : "unknow";判断char* 类型的指针是不是为空,不等于空,返回你描述的错误,等于空,返回"unknow exception"(如果没有传参,返回未知错误"unknow")
引发标准库中的异常 - - - 内存申请失败
-
发现代码出现abort( )错误,可以通过这种方式找到,这里是针对内存申请失败做了单一处理,如果不做处理,会直接调用abort()函数终止程序
-
c++ 整个异常处理机制中有一个专门的类去做管理,和多态的设计方式一样,存在新的问题,只需要拓展代码,而不需要修改原来代码
class Exception
{
public: //构造函数 干掉常属性
Exception(const char* ptr="UNKNOW") :ptr(const_cast<char*>(ptr)){}
virtual const char* what() const //父类中的what方法是虚函数 & 不存在异常
{
return ptr;
}
protected:
char* ptr; //用来存放字符串
};
//子类继承父类
class Bad_alloc :public Exception
{
public: //调用父类的构造函数抛出bad exception
Bad_alloc(const char* _Message = "bad exception") :Exception(_Message) {}
protected:
};
//子类继承父类
class Run_time :public Exception
{
public:
Run_time(const char* _Message = "run_time error") :Exception(_Message) {}
protected:
};
int main()
{
try
{
while (1)
{ //一直做内存申请不做释放最后一定会内存申请失败
int* p = new int[1024*1024*10];
}
}
catch (bad_alloc& object) /* 内存申请失败,调用bad_alloc 标准库中的异常
创建一个对象接收一下,子类中的what()方法调用父类中的what()方法打印*/
{
cout << object.what() << endl; //使用标准库中的异常
}
return 0;
}
/*输出*/
bad allocation //调用时抛出的 bad allocation 是子类对象调用继承下来的what()方法
//写代码出现莫名的中断 大原因是不做异常处理引发了abort函数中断程序
标准库中传两个参数,一个参数起到标识作用,由于:引发了不同的错误,不同错误对应了不同的错误编码 对这些错误有特定的描述 ---> 工具 ---> 错误查找 ---> 输入错误编码
值:3
错误信息:系统找不到指定的路径